![]() |
#1 |
Участник
|
mfp: X++ Debugging Tips and Tricks #2 – xSession::xppCallStack()
Источник: http://blogs.msdn.com/b/mfp/archive/...callstack.aspx
============== ![]() In the first trick we saw how to set a breakpoint in the add method on the info class. This enables us to get the context of a single error. But sometimes error messages come in hoards and stepping through each of them is not practical. Suppose you have hundreds or even thousands of errors occurring, and you want to trace where they are coming from. This trick shows you how. This trick requires code instrumentation – I will strongly discourage using this trick on a production system. Only use this trick in a test or development sandbox! The system APIs available in Dynamics AX are fantastic. There is hidden gem of an API that gives access to the current X++ call stack in the form of a container. This makes it relatively simple to include the call stack in the error messages shown in the Infolog. Just insert this line of X++ code in the add method in the info class just after the variable declaration section: _txt += con2Str(xSession::xppCallStack()); Now run the scenario, and inspect the Infolog. It will include the call stack for each message, and will look something like this: ![]() With this information it is much easier to browse through the many messages in the Infolog and find variations in the call stacks. This post is provided AS-IS, and confers no rights or warranties. ============== Источник: http://blogs.msdn.com/b/mfp/archive/...callstack.aspx
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. |
|
![]() |
#2 |
Участник
|
А теперь запустите ваш код, дергающий xSession::xppCallStack(), в IL - и обломитесь...
|
|
|
За это сообщение автора поблагодарили: Stitch_MS (1), S.Kuskov (3). |
![]() |
#3 |
Участник
|
|
|
![]() |
#4 |
Участник
|
|
|
|
За это сообщение автора поблагодарили: trud (1), S.Kuskov (3). |
![]() |
#5 |
Участник
|
спасибо, я знаю, как получить стек вызова в управляемом коде
![]() |
|
![]() |
#6 |
Участник
|
Цитата:
Попробовал набросать метод замену. Расположил в классе Session. X++: /// <summary> /// Splits the given String by the specified separator and returns the contents in a container. /// GRD_Stuff_pkoz, Перенос "наработок" из 2009-й, pkoz, 22.11.2015 /// </summary> /// <param name="_value"> /// The String value to be split. /// </param> /// <param name="_sep"> /// The separator. /// </param> /// <param name="_convertNumericToInt64"> /// A Boolean value that indicates whether to convert the value to an <c>int64</c> value; optional. /// </param> /// <param name="_unpackLineNumber"> /// A Boolean value that indicates whether to extract line number from stacktrace string _value; optional. /// </param> /// <returns> /// A container, which has the String split by the specified separator. /// </returns> /// <remarks> /// In the <paramref name="_convertNumericToInt64" /> parameter, a value of true indicates the returned /// contents will be converted to an <c>int64</c> value if they only have numeric characters.A value of /// false indicates that the returned values will not be converted to an <c>int64</c> value and String /// values will always be returned. The default value of the <paramref name="_convertNumericToInt64" /> /// parameter is true. /// </remarks> static container stackLine2con(str _value, str 10 _sep = ',', boolean _convertNumericToInt64 = true, boolean _unpackLineNumber = false) { int length = strLen(_value); int separatorLength = strLen(_sep); int i = 1; int j = strScan(_value, _sep, 1, length); container ret; void add2Ret(str _current) { if (_convertNumericToInt64 && (match('<:d+>', _current))) { ret += str2int64(_current); } else { ret += _current; } //GRD_Stuff_pkoz, Перенос "наработок" из 2009-й, pkoz, 22.11.2015 --> if ( _unpackLineNumber ) { ret += 0; // пока не реализовано } else { ret += 0; } //GRD_Stuff_pkoz, Перенос "наработок" из 2009-й, pkoz, 22.11.2015 <-- } while (j) { add2Ret(subStr(_value, i, j-i)); i = j + separatorLength; j = strScan(_value, _sep, i, length); } add2Ret(subStr(_value, i, length-i+1)); return ret; } /// </param> /// <param name="_mode"> /// An Int value that indicates how to convert the value to container; optional. /// </param> /// <returns> /// A container, which has the stack trace. /// </returns> /// <remarks> /// /// </remarks> client server public static container xppILCallStack(int _mode = 0) { str stack; container ret; // int startPos; #define.delimiter(" at ") ; if (!xSession::isCLRSession()) return xSession::xppCallStack(); new InteropPermission(InteropKind::ClrInterop).assert(); stack = System.Environment::get_StackTrace(); // startPos = strScan(stack, #delimiter, 1, 1000); switch (_mode) { case 0 : // режим, похожий на обычный xSession::xppCallStack() только извлечение номеров строк не сделано ret = [ (isRunningOnServer() ? "(S)" : "(C)") + "(CIL)", _mode ] + Session::stackLine2con(stack, #delimiter, false, false); break; case 1 : // быстрый режим, чтобы не тратить процессорное время на обработку строк. Использующий код в дальнейшем сам обработает как надо - распакует и.т.п. А скорее всего просто проигнорирует. ret = [ (isRunningOnServer() ? "(S)" : "(C)") + "(CIL)", _mode ] + [stack, 0]; break; } return ret; } Кто как решает проблему получения стека вызовов при исполнении в p-code и CIL ? Последний раз редактировалось Logger; 23.11.2015 в 12:40. Причина: исправил ошибки |
|
|
За это сообщение автора поблагодарили: Ace of Database (5). |
![]() |
#7 |
Участник
|
Один из вариантов решения приведен ниже. Поскольку стек вызова, как правило, выводится в info.add() после вызова методов Global::error(), warning(), info(), упоминание этих методов вырезается с помощью регулярных выражений из верхушки стека вызовов, чтобы не замусоривать его неинформативными строками. Метод можно вызывать как в X++, так и в CIL, правда, обработка параметра _skipLevels реализована только для X++
X++: #define.PathPrefixLen (3) #localmacro.ClientPrefix '(C)' #endmacro #localmacro.ServerPrefix '(S)' #endmacro #localmacro.RegexPatternFmt '^\\s*\\S+\\s+\%1\\.\%2.+\%1\\.(\%3\\.\%4|\%5\\.(\%6|\%7|\%8))\\(.+?\\n(.*)' #endmacro #define.RegexResultStr ('$3') #define.DotNetAppNamespace ('Dynamics.Ax.Application') public static void callStack2infolog(str _prefix = "@SYS65092", Counter _skipLevels = 0) { System.Text.RegularExpressions.MatchCollection matches; System.Text.RegularExpressions.Match oneMatch; System.Diagnostics.StackTrace stackTrace; container stack; TreeNodePath correctPath; TreeNodePath stackPath; Counter n; str regexPattern; str clrStack; int skipLevels = max(0, _skipLevels); int line; ; setPrefix(_prefix); if (xSession::isCLRSession()) { new InteropPermission(InteropKind::ClrInterop).assert(); stackTrace = new System.Diagnostics.StackTrace(true); clrStack = stackTrace.ToString(); stackTrace = null; regexPattern = strFmt( #RegexPatternFmt, strReplace(#DotNetAppNamespace, '.', '\\.'), strReplace(funcName(), '::', '\\.'), classStr(Info), methodStr(Info, Add), classStr(Global), staticMethodStr(Global, error), staticMethodStr(Global, warning), staticMethodStr(Global, info) ); matches = System.Text.RegularExpressions.Regex::Matches(clrStack, regexPattern, System.Text.RegularExpressions.RegexOptions::Singleline); n = matches.get_Count(); if (n > 0) { oneMatch = matches.get_Item(0); clrStack = oneMatch.Result(#RegexResultStr); oneMatch = null; } matches = null; CodeAccessPermission::revertAssert(); info(clrStack); } else { stack = xSession::xppCallStack(); for (n = 3 + skipLevels * 2; n <= conLen(stack); n += 2) { stackPath = conPeek(stack,n); line = conPeek(stack,n+1); correctPath = stackPath; if (line) { if ( substr(stackPath, 1, #PathPrefixLen) == #ClientPrefix || substr(stackPath, 1, #PathPrefixLen) == #ServerPrefix ) { correctPath = substr(stackPath, #PathPrefixLen + 1, maxint()); } info(strFmt('%1 %2', stackPath, line),'', SysInfoAction_Editor::newLineColumn(correctPath, line)); } else { info(strFmt('%1', stackPath)); } } } } Последний раз редактировалось gl00mie; 23.11.2015 в 12:37. Причина: поправил макрос PathPrefixLen |
|
|
За это сообщение автора поблагодарили: Logger (5). |
Теги |
callstack, debug, debug code, debug managed code, mfp, stack trace, законченный пример, полезное |
|
|