diff options
Diffstat (limited to 'src/ToolBox/SOS/Strike/WatchCmd.cpp')
-rw-r--r-- | src/ToolBox/SOS/Strike/WatchCmd.cpp | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/src/ToolBox/SOS/Strike/WatchCmd.cpp b/src/ToolBox/SOS/Strike/WatchCmd.cpp new file mode 100644 index 0000000000..443f1dd6ef --- /dev/null +++ b/src/ToolBox/SOS/Strike/WatchCmd.cpp @@ -0,0 +1,331 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#include "WatchCmd.h" + +#ifndef IfFailRet +#define IfFailRet(EXPR) do { Status = (EXPR); if(FAILED(Status)) { return (Status); } } while (0) +#endif + +_PersistList::~_PersistList() +{ + PersistWatchExpression* pCur = pHeadExpr; + while(pCur != NULL) + { + PersistWatchExpression* toDelete = pCur; + pCur = pCur->pNext; + delete toDelete; + } +} + +WatchCmd::WatchCmd() : +pExpressionListHead(NULL) +{ } +WatchCmd::~WatchCmd() +{ + Clear(); + PersistList* pCur = pPersistListHead; + while(pCur != NULL) + { + PersistList* toDelete = pCur; + pCur = pCur->pNext; + delete toDelete; + } +} + +// Deletes all current watch expressions from the watch list +// (does not delete persisted watch lists though) +HRESULT WatchCmd::Clear() +{ + WatchExpression* pCurrent = pExpressionListHead; + while(pCurrent != NULL) + { + WatchExpression* toDelete = pCurrent; + pCurrent = pCurrent->pNext; + delete toDelete; + } + pExpressionListHead = NULL; + return S_OK; +} + +// Adds a new expression to the active watch list +HRESULT WatchCmd::Add(__in_z WCHAR* pExpression) +{ + WatchExpression* pExpr = new WatchExpression; + if(pExpr == NULL) + return E_OUTOFMEMORY; + wcsncpy_s(pExpr->pExpression, MAX_EXPRESSION, pExpression, _TRUNCATE); + pExpr->pNext = NULL; + + WatchExpression** ppCurrent = &pExpressionListHead; + while(*ppCurrent != NULL) + ppCurrent = &((*ppCurrent)->pNext); + *ppCurrent = pExpr; + return S_OK; +} + +// removes an expression at the given index in the active watch list +HRESULT WatchCmd::Remove(int index) +{ + HRESULT Status = S_FALSE; + WatchExpression** ppCurrent = &pExpressionListHead; + for(int i=1; *ppCurrent != NULL; i++) + { + if(i == index) + { + WatchExpression* toDelete = *ppCurrent; + *ppCurrent = (*ppCurrent)->pNext; + delete toDelete; + Status = S_OK; + break; + } + ppCurrent = &((*ppCurrent)->pNext); + + } + return Status; +} + +// Evaluates and prints a tree version of the active watch list +// The tree will be expanded along the nodes in expansionPath +// Optionally the list is filtered to only show differences from pFilterName (the name of a persisted watch list) +HRESULT WatchCmd::Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z WCHAR* pFilterName) +{ + HRESULT Status = S_OK; + INIT_API_EE(); + INIT_API_DAC(); + EnableDMLHolder dmlHolder(TRUE); + IfFailRet(InitCorDebugInterface()); + + PersistList* pFilterList = NULL; + if(pFilterName != NULL) + { + pFilterList = pPersistListHead; + while(pFilterList != NULL) + { + if(_wcscmp(pFilterList->pName, pFilterName)==0) + break; + pFilterList = pFilterList->pNext; + } + } + + PersistWatchExpression* pHeadFilterExpr = (pFilterList != NULL) ? pFilterList->pHeadExpr : NULL; + + WatchExpression* pExpression = pExpressionListHead; + int index = 1; + while(pExpression != NULL) + { + ExpressionNode* pResult = NULL; + if(FAILED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult))) + { + ExtOut(" %d) Error: HRESULT 0x%x while evaluating expression \'%S\'", index, Status, pExpression->pExpression); + } + else + { + //check for matching absolute expression + PersistWatchExpression* pCurFilterExpr = pHeadFilterExpr; + while(pCurFilterExpr != NULL) + { + if(_wcscmp(pCurFilterExpr->pExpression, pResult->GetAbsoluteExpression())==0) + break; + pCurFilterExpr = pCurFilterExpr->pNext; + } + + // check for matching persist evaluation on the matching expression + BOOL print = TRUE; + if(pCurFilterExpr != NULL) + { + WCHAR pCurPersistResult[MAX_EXPRESSION]; + FormatPersistResult(pCurPersistResult, MAX_EXPRESSION, pResult); + if(_wcscmp(pCurPersistResult, pCurFilterExpr->pPersistResult)==0) + { + print = FALSE; + } + } + + //expand and print + if(print) + { + if(index == expansionIndex) + pResult->Expand(expansionPath); + PrintCallbackData data; + data.index = index; + WCHAR pCommand[MAX_EXPRESSION]; + swprintf_s(pCommand, MAX_EXPRESSION, L"!watch -expand %d", index); + data.pCommand = pCommand; + pResult->DFSVisit(EvalPrintCallback, (VOID*)&data); + } + delete pResult; + } + pExpression = pExpression->pNext; + index++; + } + return Status; +} + +// Deletes an persisted watch list by name +HRESULT WatchCmd::RemoveList(__in_z WCHAR* pListName) +{ + PersistList** ppList = &pPersistListHead; + while(*ppList != NULL) + { + if(_wcscmp((*ppList)->pName, pListName) == 0) + { + PersistList* toDelete = *ppList; + *ppList = (*ppList)->pNext; + delete toDelete; + return S_OK; + } + ppList = &((*ppList)->pNext); + } + return S_FALSE; +} + +// Renames a previously saved persisted watch list +HRESULT WatchCmd::RenameList(__in_z WCHAR* pOldName, __in_z WCHAR* pNewName) +{ + if(_wcscmp(pOldName, pNewName)==0) + return S_OK; + PersistList** ppList = &pPersistListHead; + while(*ppList != NULL) + { + if(_wcscmp((*ppList)->pName, pOldName) == 0) + { + PersistList* pListToChangeName = *ppList; + RemoveList(pNewName); + wcsncpy_s(pListToChangeName->pName, MAX_EXPRESSION, pNewName, _TRUNCATE); + return S_OK; + } + ppList = &((*ppList)->pNext); + } + return S_FALSE; +} + +// Saves the active watch list together with the current evaluations as +// a new persisted watch list +HRESULT WatchCmd::SaveList(__in_z WCHAR* pSaveName) +{ + HRESULT Status = S_OK; + INIT_API_EE(); + INIT_API_DAC(); + IfFailRet(InitCorDebugInterface()); + + RemoveList(pSaveName); + PersistList* pList = new PersistList(); + wcsncpy_s(pList->pName, MAX_EXPRESSION, pSaveName, _TRUNCATE); + pList->pHeadExpr = NULL; + PersistCallbackData data; + data.ppNext = &(pList->pHeadExpr); + WatchExpression* pExpression = pExpressionListHead; + while(pExpression != NULL) + { + ExpressionNode* pResult = NULL; + if(SUCCEEDED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult))) + { + pResult->DFSVisit(PersistCallback, (VOID*)&data); + delete pResult; + } + pExpression = pExpression->pNext; + } + + pList->pNext = pPersistListHead; + pPersistListHead = pList; + return Status; +} + +// Saves the current watch list to file as a sequence of commands that will +// recreate the list +HRESULT WatchCmd::SaveListToFile(FILE* pFile) +{ + WatchExpression* pExpression = pExpressionListHead; + while(pExpression != NULL) + { + fprintf_s(pFile, "!watch -a %S\n", pExpression->pExpression); + pExpression = pExpression->pNext; + } + return S_OK; +} + +// Escapes characters that would be interpretted as DML markup, namely angle brackets +// that often appear in generic type names +VOID WatchCmd::DmlEscape(__in_ecount(cchInput) WCHAR* pInput, int cchInput, __in_ecount(cchOutput) WCHAR* pEscapedOutput, int cchOutput) +{ + pEscapedOutput[0] = L'\0'; + for(int i = 0; i < cchInput; i++) + { + if(pInput[i] == L'<') + { + if(0 != wcscat_s(pEscapedOutput, cchOutput, L"<")) return; + pEscapedOutput += 4; + cchOutput -= 4; + } + else if(pInput[i] == L'>') + { + if(0 != wcscat_s(pEscapedOutput, cchOutput, L">")) return; + pEscapedOutput += 4; + cchOutput -= 4; + } + else if(cchOutput > 1) + { + pEscapedOutput[0] = pInput[i]; + pEscapedOutput[1] = '\0'; + pEscapedOutput++; + cchOutput--; + } + if(pInput[i] == L'\0' || cchOutput == 1) break; + } +} + +// A DFS traversal callback for the expression node tree that prints it +VOID WatchCmd::EvalPrintCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData) +{ + PrintCallbackData* pData = (PrintCallbackData*)pUserData; + for(int i = 0; i < depth; i++) ExtOut(" "); + if(depth == 0) + ExtOut(" %d) ", pData->index); + else + ExtOut(" |- "); + if(pExpressionNode->GetErrorMessage()[0] != 0) + { + ExtOut("%S (%S)\n", pExpressionNode->GetRelativeExpression(), pExpressionNode->GetErrorMessage()); + } + else + { + // names can have '<' and '>' in them, need to escape + WCHAR pEscapedTypeName[MAX_EXPRESSION]; + DmlEscape(pExpressionNode->GetTypeName(), (int)_wcslen(pExpressionNode->GetTypeName()), pEscapedTypeName, MAX_EXPRESSION); + WCHAR pRelativeExpression[MAX_EXPRESSION]; + DmlEscape(pExpressionNode->GetRelativeExpression(), (int)_wcslen(pExpressionNode->GetRelativeExpression()), pRelativeExpression, MAX_EXPRESSION); + DMLOut("%S <exec cmd=\"%S (%S)%S\">%S</exec> %S\n", pEscapedTypeName, pData->pCommand, pEscapedTypeName, pExpressionNode->GetAbsoluteExpression(), pRelativeExpression, pExpressionNode->GetTextValue()); + } +} + +// A DFS traversal callback for the expression node tree that saves all the values into a new +// persisted watch list +VOID WatchCmd::PersistCallback(ExpressionNode* pExpressionNode, int depth, VOID* pUserData) +{ + PersistCallbackData* pData = (PersistCallbackData*)pUserData; + if(depth != 0) + return; + + PersistWatchExpression* pPersistExpr = new PersistWatchExpression(); + wcsncpy_s(pPersistExpr->pExpression, MAX_EXPRESSION, pExpressionNode->GetAbsoluteExpression(), _TRUNCATE); + FormatPersistResult(pPersistExpr->pPersistResult, MAX_EXPRESSION, pExpressionNode); + pPersistExpr->pNext = NULL; + *(pData->ppNext) = pPersistExpr; + pData->ppNext = &(pPersistExpr->pNext); +} + +// Determines how the value of an expression node is saved as a persisted result. This effectively determines +// the definition of equality when determining if an expression has changed value +VOID WatchCmd::FormatPersistResult(__inout_ecount(cchPersistResult) WCHAR* pPersistResult, DWORD cchPersistResult, ExpressionNode* pExpressionNode) +{ + if(pExpressionNode->GetErrorMessage()[0] != 0) + { + _snwprintf_s(pPersistResult, MAX_EXPRESSION, _TRUNCATE, L"%s (%s)\n", pExpressionNode->GetRelativeExpression(), pExpressionNode->GetErrorMessage()); + } + else + { + _snwprintf_s(pPersistResult, MAX_EXPRESSION, _TRUNCATE, L"%s %s %s\n", pExpressionNode->GetTypeName(), pExpressionNode->GetRelativeExpression(), pExpressionNode->GetTextValue()); + } +} |