Как определить, где возвращается длинная функция - PullRequest
5 голосов
/ 21 августа 2009

Предположим, что есть функция с кодом 1000 строки с именем LongFunction, и мы ее использовали:

bool bSuccess = LongFunction();
assert(bSuccess);

Здесь я получил утверждение при отладке, и я знаю, что с LongFunction что-то не так, поэтому мне нужно найти, где функция встречает проблемы и возвращает:

  1. Возможно, я мог бы отладить его шаг за шагом, он работает, но отнимает много времени, нам не нужно делать это таким образом.

  2. Я мог бы выполнить поиск по ключевому слову «return» (или даже более точному поиску, используя RegExp) и установить точку останова для этих возвратов, это должно быть быстрее, но это все еще утомительная ручная работа, которую невозможно автоматизировать.

  3. # определить return TRACE ( LINE ); вернуться

Работает, но есть следующие проблемы:

  • Он напечатает слишком много избыточной информации, так как return часто используется (или мы могли бы использовать EnvVar для включения или выключения)
  • Не работает для следующего случая: if (bOK) возвращает true;

У вас есть другие творческие идеи о том, как точно определить проблему?

Edit: Вот некоторые детали, которые позволят нам сосредоточиться на проблеме.

  1. Речь идет о C ++, а не о платформе.

  2. Мы не хотим реорганизовывать функции (да, я знаю, что должны), мы даже не хотим изменять какой-либо код - на данный момент мы просто хотим предоставить некоторые средства для отладки нашего приложения. Полегче. Я также считаю, что это должно быть общим требованием, вы никогда не сталкивались с этим?

  3. LongFunction () имеет несколько точек выхода, и тип возвращаемого значения не обязательно bool (HRESULT, пользовательский код ошибки ...)

Редактировать : Сводка текущих обсуждений:
У нас есть некоторые противоречия:

  1. Вы должны рефакторинг функции.
    Да, все знают, что мы должны, но это не главное. Если бы я сделал вызов для рефакторинга функции, я бы не был здесь, чтобы задать вопрос.

  2. Найти, где LongFunction () возвращает ошибку, не помогает.
    Я всегда первым делом обнаруживаю, где происходит ошибка, чтобы узнать, что произошло, мне любопытно, почему это не помогает, что вы делали в этой ситуации? (Предположим, я уже знаком с тем, как работает эта функция)

И у нас есть 2 разумных решения:

  1. ReturnMarker из Crashworks, стековый объект в функции будет разрушаться при выходе из функции, установка точки останова в деструкторе покажет вам, где он возвращается в отладчике

  2. CMyBool (x) из Binary & Sadsido, измените тип возврата LongFunction на CMyBool, который может конструироваться из bool, возврат из LongFunction создаст этот объект, так что просто установите точку останова в конструкторе.

Ответы [ 9 ]

13 голосов
/ 22 августа 2009

Очевидно, что вам следует реорганизовать эту функцию, но в C ++ вы можете использовать этот простой способ для решения этой проблемы за пять минут:

class ReturnMarker
{
   public:
   ReturnMarker()  {};
   ~ReturnMarker() 
   {
      dummy += 1; //<-- put your breakpoint here
   }
   static int dummy;
}

int ReturnMarker::dummy = 0;

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

void LongFunction()
{
    ReturnMarker foo;

    // ...
}
8 голосов
/ 21 августа 2009

Похоже, пришло время провести рефакторинг LongFunction () ...

Функция 1000 строк - это неприятный запах кода. Проведите время, превращая его в более мелкие, более удобные в обслуживании функции. Пока вы будете в нем, вы найдете ошибку (и), и это будет достойное вложение в будущее.

4 голосов
/ 21 августа 2009

Если ваша проблема просто лень (кстати, ничего страшного), убедитесь, что все операторы возврата в LongFunction имеют форму

return(value);

вместо

return value;

(например, используя регулярное выражение поиска и замены)

Затем используйте слегка измененный макрос препроцессора, отличный от первоначального предложения:

#define return(value) { if (!value) TRACE(__LINE__); return(value); }

... или даже

#define return(value) { assert(value); return(value); }

... или что вы считаете уместным

4 голосов
/ 21 августа 2009

Это C или C ++?

Если C ++, создайте новый класс, который оборачивает bool (скажем, CMyBool), который имеет автоматическое приведение к bool.

Теперь LongFunction возвращает CMyBool (при быстром поиске и замене все возвраты в LongFuntion изменятся на «возврат CMyBool (x)».

Теперь установите точку останова в ctor для CMyBool, отладчик теперь остановится при создании CMyBool, что будет в правильном операторе возврата в LongFunction.

Автоматическое приведение к bool остановит CMyBool от взлома кода, использующего CMyBool.

Это избавит вас от первоначальной проблемы, но большая проблема заключается в том, что LongFunction нуждается в рефакторинге.

Надеюсь, это поможет.

0 голосов
/ 22 августа 2009

В 1982 году мне было поручено исправить неисправную программу потоковой диаграммы. Программа, написанная на машинно-зависимом Harris FORTRAN (сейчас я не помню, была ли она на FORTRAN IV или FORTRAN 77), состояла из основной программы на 1100 строк, подпрограммы на 900 строк и около дюжины подпрограмм, каждая из которых была От 10 до 20 строк.

О, и в программе почти не было пробелов (пустых строк), и комментарии были бесполезны.

Мне потребовалось 160 часов - четыре недели ПОЛНОЕ ВРЕМЯ, когда НИЧЕГО не было на моем столе - чтобы получить этот код в достаточной степени, чтобы произвести надлежащий ремонт.

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

Найти данные достаточно просто. Для Microsoft AbysmalC ++: ищите каждый экземпляр «return» и начинайте его с

»printf (« \ n \ n --- >>> пробивая в строке% d \ n \ n », __LINE__) ; "

(или эквивалент в вашей системе). Очевидно, вы не можете просто сделать это автоматически; вам придется посмотреть на местный брекетинг. Затем запустите свой тест и посмотрите, где он говорит вам искать.

В том-то и дело, что в реальном мире вычислений практически нет реальных подпрограмм, длина которых в действительности должна составлять 1000 строк. За почти 40 лет в этой ракетке, будучи студентом и профессионалом, я столкнулся с ровно одной рутиной, которая ДОЛЖНА быть длиннее, чем одна страница принтера (около 60 строк), и это была особая ситуация. Всего было около трех страниц.

С другой стороны, я видел много запущенных модулей, где тот, кто написал его, был слишком ленивым или слишком некомпетентным, чтобы правильно его учитывать, а программисты по обслуживанию были слишком ленивы, слишком некомпетентны, или слишком напуган своим менеджером, чтобы вернуться и реорганизовать его.

Наконец: учтите, что, вероятно, это будет не последний раз, когда кто-то должен работать над этим модулем, и, возможно, это будет не последний раз, когда вам придется работать над ним. Подобные вещи обычно бывают смоляными: когда вы прикасаетесь к ним, вы никогда не отклеиваетесь от них. Вполне возможно, что стоит потратить ваше время на рефакторинг, по крайней мере, и, вполне возможно, перепроектировать / переписать его из SCRATCH и первых принципов.

0 голосов
/ 22 августа 2009

«Возможно, я мог бы отладить его шаг за шагом, он работает, но отнимает много времени, мы не знаем, что делать так».

Как еще вы отлаживаете?

Я использовал для отладки:

1) поиск набора параметров, воспроизводящих проблему.

2) пошагово выполнить код с этим набором параметров.

Если есть 1000 строк кода, как вы собираетесь «рефакторинг», не зная ТОЧНО, что функция делает и должна делать.

И как ты собираешься делать это, не переходя через функцию. Я подумал, что для этого нужна IDE с хорошим отладчиком.

Откровенно говоря, я нахожу вопрос почти смешным и очень грустным.

0 голосов
/ 21 августа 2009

В визуальной студии я бы установил точку останова на assert, затем, используя окно трассировки стека, кликнул на следующую строку вверх, и вы попадете в точку выхода метода.

0 голосов
/ 21 августа 2009

Вы не сказали, на какой платформе вы работаете. В зависимости от содержимого LongFunction, вот как я подхожу к этому, используя gdb:

Давайте представим, что ваш файл 'f.cc' имеет следующие строки:

1: bool LongFunction () {  /* ... */ }
2:
3: void bar ()
4: {
5:   bool bSuccess = LongFunction ();
6:   assert (bSuccess);
7: }

Вот шаги в GDB:

  1. Добавить точку останова на той же строке, что и утверждение: break f.cc:6

  2. Добавить условие к этой точке останова, если bSuccess имеет значение false: condition 1 (bSuccess==0)

  3. Запустите вашу программу до достижения этой точки останова

  4. Установить точку останова в начале тела функции: break f.cc:4

  5. Перейти обратно в это место (и оно остановится в точке останова): jump f.cc:4

  6. Отладьте содержимое LongFunction, чтобы увидеть причину его сбоя.

Причина, по которой я говорю, что это зависит от содержимого LongFunction, заключается в том, что если LongFunction читает входные данные из потока или изменяет глобальные переменные и т. Д., То его поведение во второй раз может отличаться. Вы должны рассмотреть вышеупомянутые шаги как то же самое, как если бы код был:

3: void bar ()
4: {
5:   bool bSuccess = LongFunction ();
5:   bSuccess = LongFunction ();
6:   assert (bSuccess);
7: }
0 голосов
/ 21 августа 2009
#define return { TRACE(LINE); return; }

Это решает вашу проблему 4.

Что касается остального, то это просто проблема с кодированием. Вот почему многие системы возвращают более сложные ошибки (например, HRESULT из COM-объекта) и / или спам в поток отладки при возникновении проблемы.

Функция 1000 строк должна быть изменена. Поскольку вы видите, что длинная функция оказывается невероятно сложной для обслуживания.

Изменить: Будет ли следующий работать лучше, чем выше?

#define return TRACE(LINE), return

Пили несколько напитков, так что может и нет.

...