none
WPF: Как извлечь из стека значения аргументов ранее вызванных функций? RRS feed

  • Вопрос

  • Доброго дня!

    По ходу программы понадобилась дебажная функция, дающая на Output информацию о цепочке вызовов. Написал её так:

    DefaultTraceListener DefTrace = new DefaultTraceListener();

    ...

    public void ShowDebug(string Text, int StackHistory = 4) { DateTime dt = DateTime.Now; string msg = String.Format("{0}:{1}.{2} ", dt.Minute, dt.Second, dt.Millisecond); for (int i = StackHistory; i > 0; i--) { StackFrame sf = new StackFrame(i); MethodBase met = sf.GetMethod(); msg += met.DeclaringType.Name + '.' + met.Name + " => "; } DefTrace.WriteLine(msg + Text); }

    И всё бы хорошо, но тут не выводятся значения параметров функций из цепочки. Метод met.GetParameters() может вернуть список параметров нужных функций, но там отобразятся не значения их, а только типы. Как увидеть значения?

    Заранее большое спасибо!

    • Изменено SvarogichRed 15 апреля 2013 г. 8:19 улучшение читаемости кода
    15 апреля 2013 г. 8:17

Ответы

Все ответы

  • Здравствуйте! Интересный вопрос. На самом деле сделать это почти нельзя. Значения локальных переменных и ссылки из стека удаляются после возврата из метода, кроме того за кулисами происходит много чего, что мы не видим. Если методы принадлежат не вам, а являются частью .Net, то вы можете войти в код и посмотреть эти значения во время отладки или выставить точки останова в нужных местах с привязкой к матодам вывода в окно Output (чтобы как то автоматизировать этот процесс). Как это сделать, написано тут. максимум, что можно сделать, это получить информацию о параметрах.
    using System;
    using System.Diagnostics;
    using System.Reflection;
    
    namespace ConsoleApplication
    {
      class Program
      {
        static DefaultTraceListener DefTrace = new DefaultTraceListener();
        static void Main(string[] args)
        {
          Trace.Listeners.Add(DefTrace);
          DefTrace.LogFileName = @"c:\myListener.log";
          F(1, 2);
        }
        public static void ShowDebug(string Text, int StackHistory = 2)
        {
          DateTime dt = DateTime.Now;
          string msg = String.Format("{0}:{1}.{2} ", dt.Minute, dt.Second, dt.Millisecond);
          for(int i = StackHistory; i > 0; i--)
          {
            StackFrame sf = new StackFrame(i);
            MethodBase met = sf.GetMethod();
            msg += met.DeclaringType.Name + '.' + met.Name + " => ";
            var args = met.GetParameters();
            foreach(var a in args)
            {
              msg += " " + a.Name;
            }
          }
          DefTrace.WriteLine(msg + Text);
        }
        static int F(int a, int b)
        {
          ShowDebug("dsd");
          return 4;
        }
      }
    }

    15 апреля 2013 г. 9:41
    Модератор
  • Значения локальных переменных и ссылки из стека удаляются после возврата из метода
    Секундочку. Возврата из метода ещё не произошло. Вот в приведённом Вами примере Main вызывает F, а F вызывыет ShowDebug. И Main, и F ещё не закончились. Вся инфа о них должна оставаться в стеке.
    16 апреля 2013 г. 4:05
  • Дело в том, что понятие стек не одно. В данном случае вы говорите о стеке вызовов, который хранит информацию о вызовах и никакого отношения не имет к вычислениям. А я о стеке вычислений(Evaluation stack), каторый один, для каждого метода и в нём производятся вычисления конкретного метода после вызова он очищается и из вне(другого метода) к нему нет доступа (и всё это виртуально, так как физически всё компилируется в x86 код). Хотя он же существует в методе, пока вызов внутреннего метода не завершится. Т.е. в нашем случае метод ShowDebug не может достучаться до этих данных метода F. А вот аргументы метода хранятся в отдельной памяти (хранятся либо сами значения, либо ссылки) который тоже принадлежит только одному методу. Как их достать, и можно ли это думаю нет. Может есть такая возможность о которой знают раработчики CLR, и не знаем мы. Думаю, что уже понятно, что тип StackFrame хранит только информацию о вызовах и типах, но не сами значения.

    Поэтому, если вам нужно мониторить значения локальных переменных, то это нужно делать из самого метода, т.е. стек вызовов эту инфу не даст.

    17 апреля 2013 г. 6:34
    Модератор
  • Спасибо.

    Ну, пути ВПФ неисповедимы, конечно. Однако я уверен, что достать значения можно, т.к. сама среда IDE VisualStudio их достает и показывает в окошке Call Stack. 

    Возможно, путь к этому и непрост, но он есть. Пошукаю ещё.

    17 апреля 2013 г. 9:59
  • Привет

    Вот здесь и здесь пишут, что это не возможно сделать используя StackFrame, т.к. скорее всего он не содержит данные, а хранит только сигнатуры функций, которые вызывали. Там же дается ссылка на возможный вариант решения проблемы.

    Еще один вариант рассмотрен здесь - Can I get parameter names/values procedurally from the currently executing function?


    Для связи [mail]

    17 апреля 2013 г. 18:05
  • Спасибо. Походил по ссылкам, посмотрел. Выходит, что без сторонних средств получить значения действительно нельзя. И сам IDE тоже внешними дебаггерами пользуется. Ну ладно, не смертельно :)
    18 апреля 2013 г. 11:26
  • Я конечно не разработчик студии, но из того, что я знаю она достаёт значения этих переменных во время останова, что не проблема. А вот во время мониторинга, когда пишется полный стек, я вообще не встречал подобных возможностей. Хотя, повторюсь, может и есть недокументированные возможности.
    18 апреля 2013 г. 11:31
    Модератор