Исправлена ​​ошибка с четырьмя nops в if (0), мир больше не имеет смысла - PullRequest
6 голосов
/ 02 апреля 2009

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

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

Следующее, вставленное где-либо в тело функции, но нигде в программе, исправляет это:

if(0) {
    __asm__("nop\n");
    __asm__("nop\n");
    __asm__("nop\n");
    __asm__("nop\n");
}

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

Пожалуйста, помогите мне разобраться в мире! Мне не хочется писать это в GCC, так как первое правило отладки - не винить компилятор. Но, черт возьми, я собираюсь. Я использую Mac OS 10.5 на башне G5, и рассматриваемый компилятор идентифицирует себя как 'powerpc-apple-darwin9-gcc-4.0.1', но я думаю, что это может быть самозванцем ...

ОБНОВЛЕНИЕ: Curiouser и curiouser ... Я отправил файлы .s с nops и без. Не только слишком много различий, которые нужно проверить, но и без каких-либо проблем файл .s имеет размер 196 620 байт, а также 156 719 байт. (!)

ОБНОВЛЕНИЕ 2: Ух ты, должен был выложить код! Я вернулся к коду сегодня свежим взглядом и сразу увидел ошибку. Смотрите мой зову себя ответ ниже.

Ответы [ 9 ]

15 голосов
/ 02 апреля 2009

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

13 голосов
/ 02 апреля 2009

Это неверная арифметика с указателями, либо напрямую (через указатель), либо косвенно (проходя через конец массива). Проверьте все ваши массивы. Не забывайте, что если ваш массив

 int a[4];

тогда [4] не существует.

То, что вы делаете, случайно перезаписывает что-то в стеке. Стек содержит как локальные, параметры, так и адрес возврата из вашей функции. Вы можете повредить обратный адрес так, чтобы излечить лишние noops.

Например, если у вас есть код, который добавляет что-то к адресу возврата, вставка этих дополнительных 16 байтов noops излечит проблему, потому что вместо возврата за следующую строку кода вы возвращаетесь в середину Без операционное.

Один из способов добавить что-то к адресу возврата - пройти через конец локального массива или параметра, например

  int a[4];
  a[4]++;
6 голосов
/ 07 апреля 2009

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

Основная проблема заключалась в том, что я пропустил операторы return в рекурсивной функции. У меня было:

bool function() {
    /* lots of code */
    function()
}

Когда это должно было быть:

bool function() {
    /* lots of code */
    return function()
}

Это сработало, потому что благодаря магии оптимизации правильное значение оказалось в нужном регистре в нужное время и попало в нужное место.

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

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

3 голосов
/ 02 апреля 2009

Можете ли вы подтвердить, что вы действительно получаете разные исполняемые файлы, когда добавляете if (0) {nops}? Я не вижу nops в моей системе.

$ gcc --version
powerpc-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5490)

$ cat nop.c
void foo()
{
    if (0) {
        __asm__("nop");
        __asm__("nop");
        __asm__("nop");
        __asm__("nop");
    }
}

$ gcc nop.c -S -O0 -o -
    .
    .
_foo:
    stmw r30,-8(r1)
    stwu r1,-48(r1)
    mr r30,r1
    lwz r1,0(r1)
    lmw r30,-8(r1)
    blr

$ gcc nop.c -S -O3 -o -
    .
    .
_foo:
    blr
3 голосов
/ 02 апреля 2009

Это происходит в режиме отладки и выпуска (с символами и без)? Это ведет себя так же, используя отладчик? Является ли код многопоточным? Вы компилируете с оптимизацией? Можете ли вы попробовать другую машину?

2 голосов
/ 02 апреля 2009

Похоже, вам нужно будет немного поработать и смазать локоть

Ваша проблема звучит похоже на то, что я отлаживал в прошлом, когда мое приложение работало регулярно ... когда из ниоткуда оно перепрыгивало в другую часть приложения, и стэк вызовов был полностью испорчен (однако это было встроенное программирование !)

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

Я бы подошел к проблеме так:

  1. Выключи свой любимый отладчик
  2. Начните шагать по своему коду и наблюдайте за стеком вызовов и локальными переменными и ищите подозрительную активность
  3. Отказ системы
  4. Сосредоточиться на том, где система отказывает

Сосредоточьтесь на повторении ваших изменений кода:

  1. внесение изменений в код, которые «приведут к сбою системы»
  2. запуск / отладка и просмотр
  3. Если он работает нормально, вы ищете / пробуете не то, и вам нужно попробовать что-то еще. Если вы ошиблись, значит, вы добились прогресса в поиске ошибки.
  4. Если вы не знаете, где или как происходит сбой системы, вы не сможете решить проблему.

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

Вот плакат из книги:

9 Rules of debugging image
(источник: google.com )


Конкретные предложения:

  1. Если вы считаете, что это компилятор, запустите другую платформу / ОС / компилятор.
  2. Как только вы исключили платформу / ОС / компилятор, попробуйте реструктурировать код. Ищите «умные» части кода и посмотрите, действительно ли они делают то, для чего предназначен код… возможно, умное решение на самом деле не было умным и делает что-то еще.
2 голосов
/ 02 апреля 2009

Я предполагаю, что повреждение стека - хотя gcc должен оптимизировать что-либо внутри if (0), я бы подумал.

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

Вы уверены, что работаете так, как вы думаете? (тупой вопрос, но это случается.)

1 голос
/ 07 апреля 2009

Я автор "Отладки", на которую любезно ссылается Тревор Бойд Смит. У него все в порядке - ключевые правила здесь: # 2 Make It Fail (что, кажется, у вас хорошо получается), и # 3 - Бросьте думать и посмотрите. Вышеприведенные гипотезы очень хороши (демонстрирует овладение правилом № 1 - Понимание системы - в этом случае способ, которым размер кода может изменить ошибку). Но на самом деле наблюдение за его ошибкой с помощью отладчика покажет вам, что на самом деле происходит без догадок.

0 голосов
/ 03 апреля 2009

Разбейте эту функцию на отдельный файл .c (или .cpp или любой другой). Скомпилируйте только один этот файл с nops и без них в файлы .s и сравните их.

Попробуйте старую версию gcc. Вернитесь на 5 или 10 лет назад и посмотрите, не станет ли что-то странным.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...