AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX Blogs
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск Все разделы прочитаны

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 02.10.2013, 15:11   #1  
Blog bot is offline
Blog bot
Участник
 
22,207 / 771 (70) +++++++
Регистрация: 28.10.2006
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, напишите личное сообщение администратору.
Старый 02.10.2013, 17:03   #2  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,591 / 5201 (181) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 2
А теперь запустите ваш код, дергающий xSession::xppCallStack(), в IL - и обломитесь...
За это сообщение автора поблагодарили: Stitch_MS (1), S.Kuskov (3).
Старый 02.10.2013, 19:45   #3  
skuull is offline
skuull
Участник
Most Valuable Professional
Лучший по профессии 2014
 
644 / 665 (24) +++++++
Регистрация: 08.03.2013
Адрес: ХЗ
Цитата:
Сообщение от gl00mie Посмотреть сообщение
А теперь запустите ваш код, дергающий xSession::xppCallStack(), в IL - и обломитесь...
Можно галочку убрать )
Старый 03.10.2013, 12:24   #4  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,288 / 2489 (91) +++++++++
Регистрация: 16.01.2004
Адрес: Москва
Записей в блоге: 5
Цитата:
Сообщение от gl00mie Посмотреть сообщение
А теперь запустите ваш код, дергающий xSession::xppCallStack(), в IL - и обломитесь...
http://letmebingthatforyou.com/?q=ge...20trace%20c%23
__________________
https://axcoder.github.io
За это сообщение автора поблагодарили: trud (1), S.Kuskov (3).
Старый 03.10.2013, 13:32   #5  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,591 / 5201 (181) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 2
спасибо, я знаю, как получить стек вызова в управляемом коде тут вопрос в том, что в контексте AX 2012 стоит указывать область применимости "старых добрых" трюков отладки, иначе получаются дырявые абстракции.
Старый 23.11.2015, 11:01   #6  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,210 / 2085 (76) +++++++++
Регистрация: 12.10.2004
Записей в блоге: 2
Цитата:
Сообщение от gl00mie Посмотреть сообщение
А теперь запустите ваш код, дергающий xSession::xppCallStack(), в IL - и обломитесь...
Как-то некрасиво x++ себя ведет в этой ситуации.
Попробовал набросать метод замену. Расположил в классе 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).
Старый 23.11.2015, 11:37   #7  
gl00mie is offline
gl00mie
Участник
MCBMSS
Most Valuable Professional
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
3,591 / 5201 (181) ++++++++++
Регистрация: 28.11.2005
Адрес: Москва
Записей в блоге: 2
Один из вариантов решения приведен ниже. Поскольку стек вызова, как правило, выводится в 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, законченный пример, полезное

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
mfp: X++ Debugging Tips and Tricks Blog bot DAX Blogs 2 24.09.2013 13:01
mfp: X++ Debugging Tips and Tricks #1 – Breakpoint in Info.add() Blog bot DAX Blogs 1 24.09.2013 11:00
sumitax: EP Debugging Tips and Tricks –Part III Blog bot DAX Blogs 0 13.10.2010 14:05
sumitax: EP Debugging Tips and Tricks –Part II Blog bot DAX Blogs 0 30.09.2010 09:05
sumitax: EP Debugging Tips and Tricks –Part I Blog bot DAX Blogs 0 13.08.2010 21:06
Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 12:34.
Powered by vBulletin® v3.8.5. Перевод: zCarot
Контактная информация, Реклама.