Раскрытие ассемблера языка кода спагетти - PullRequest
17 голосов
/ 12 июня 2009

Я унаследовал 10-строчную программу, написанную на языке ассемблера 8051, которая требует некоторых изменений. К сожалению, это написано в лучших традициях кода спагетти. Программа, написанная в виде одного файла, представляет собой лабиринт операторов CALL и LJMP (всего около 1200) с подпрограммами, имеющими несколько точек входа и / или выхода, если они вообще могут быть определены как подпрограммы. Все переменные являются глобальными. Есть комментарии; некоторые верны. Нет существующих тестов и нет бюджета для рефакторинга.

Небольшая предыстория приложения: код управляет коммуникационным узлом в торговом приложении, которое в настоящее время развернуто на международном уровне. Он обрабатывает два последовательных потока одновременно (с помощью отдельного коммуникационного процессора) и может взаимодействовать с четырьмя различными физическими устройствами, каждое из которых принадлежит разному поставщику. Производитель одного из устройств недавно внес изменение («Да, мы внесли изменение, но программное обеспечение абсолютно одинаковое!»), Что приводит к тому, что некоторые конфигурации системы перестают работать, и не заинтересовано в его изменении (каким бы оно ни было) они не изменились).

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

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

Оригинальный вопрос: мне нужно понять программу достаточно хорошо, чтобы внести изменения без поломок. Кто-нибудь разработал методы для работы с этим беспорядком?

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

Ответы [ 11 ]

17 голосов
/ 12 июня 2009

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

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

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

Если у вашего работодателя нет такой технической подготовки, попросите его пригласить другого программиста / коллегу, чтобы объяснить ему ваши шаги, это фактически покажет ему, что вы серьезно и честно относитесь к этому, потому что это реальная проблема - не только с вашей точки зрения (убедитесь, что есть коллеги, которые знают об этом «проекте»).

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

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

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

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

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

Учитывая, что код имеет размер 10 КБ, я также рассмотрел бы выделение подпрограмм в отдельные файлы, чтобы сделать компоненты более узнаваемыми, предпочтительно используя оболочки доступа вместо глобальных переменных, а также интуитивно понятные имена файлов.

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

Аналогично, огромные подпрограммы также могут быть преобразованы в более мелкие, чтобы улучшить читаемость.

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

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

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

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

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

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

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

Если не в финансовом отношении, может быть, вы предоставили ему соответствующие патчи?

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

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

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

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

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

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

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

Использование инструментов типа dot / graphviz для визуализации структуры последовательности инициализации и самого основного цикла будет чистой радостью по сравнению с выполнением всего этого вручную.

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

7 голосов
/ 12 июня 2009

Боюсь, нет волшебной пули для такого рода проблем. Я считаю, что единственное решение состоит в том, чтобы распечатать файл ASM, а затем пойти куда-нибудь тихо и симулировать выполнение программы построчно в уме (при записи содержимого регистров и областей памяти в блокноте). Через некоторое время вы обнаружите, что это не займет столько времени, сколько вы ожидаете. Будьте готовы потратить много часов на это и выпить галлоны кофе. Через некоторое время у вас появится понимание того, что он делает, и вы сможете рассмотреть изменения.

Есть ли в 8051 какие-либо неиспользуемые порты ввода-вывода? Если это так, и вы не можете работать при вызове определенных подпрограмм, то добавьте код, чтобы отправить эти запасные порты как высоко, так и низко. затем когда программа запущена, наблюдайте за этими портами с помощью осциллографа.

Удачи

6 голосов
/ 12 июня 2009

Я знаю, это звучит безумно .... но я безработный (я выбрал неподходящее время, чтобы сказать партнеру по браку идти в ад) и у меня есть немного свободного времени. Я был бы готов взглянуть на это. Раньше я писал сборку для яблока] [и оригинального ПК. Если бы я мог поиграть с вашим кодом на симуляторе в течение пары часов, я мог бы дать вам идею, если у меня будет возможность документировать его для вас (без проведения моего незапланированного отпуска). Поскольку я ничего не знаю о 8051, это может быть невозможно для кого-то вроде меня, но симулятор выглядел многообещающе. Я не хотел бы денег, чтобы сделать это. Достаточно просто получить представление о встроенной разработке 8051. Я сказал вам, это будет звучать безумно.

4 голосов
/ 12 июня 2009

Я делал подобные вещи пару раз. Некоторые рекомендации:

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

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

Удачи.

4 голосов
/ 12 июня 2009

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

3 голосов
/ 12 июня 2009

Насколько хорошо вы понимаете аппаратную платформу, на которой работает этот код?

  • Был ли он переведен в режим отключения питания (Pcon = 2) для экономии энергии Если так, как это проснулось. (сброс или аппаратное прерывание)

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

  • Был ли он переведен в спящий режим (Pcon = 1)

Существуют ли разные версии аппаратного обеспечения в полевых условиях?

Убедитесь, что у вас есть все варианты оборудования для тестирования.

Не тратьте время на симулятор - с ним очень сложно работать, и вам приходится делать множество предположений относительно аппаратного обеспечения. Получите In Circuit Emulator (ICE) и работайте на аппаратном обеспечении.

Программное обеспечение было написано на ассемблере по той причине, по которой вам нужно выяснить, почему. то есть - ограничения памяти - ограничение скорости

Возможно, причина в том, что в этом коде беспорядок

Посмотрите на файл ссылки для:

ПРОБЕЛ XDATA, ПРОБЕЛ IDATA и ПРОБЕЛ КОДА:

Если нет свободного места для кода или Xdata или Idata?

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

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

1 голос
/ 08 июля 2013

У меня была очень похожая проблема с программным обеспечением 8052. Таким образом, компания унаследовала такого зверя с полным объемом ПЗУ (64 КБ), около 1,5 мегабайта сборочных спагетти-модулей плюс два 3000-строчных PL / M-модуля, составляющих это чудовище кодирования. Первоначальные разработчики программного обеспечения были давно мертвы (это не значит, что никого не было, но на самом деле никто не понимал этого в целом), компиляторы, которые их компилировали, были с середины 80-х годов, работали на эмуляторе MDS-70, и несколько критических модули были в пределах этих компиляторов. Как добавить еще один глобальный символ, и компоновщик потерпит крах. Добавьте еще один символ в файл ASM, и компилятор будет аварийно завершен.

Так, как можно начать резать это?

Сначала вам понадобятся инструменты. Например, Notepad ++ - очень приятная вещь, поскольку его можно использовать для перекрестного поиска по нескольким файлам одновременно, что идеально подходит для поиска модулей, ссылающихся на глобальный символ. Это, наверное, самый важный элемент.

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

Чтобы получить архитектуру самостоятельно, сначала вы можете попробовать построить граф вызовов . Это проще сделать, чем граф потока данных, поскольку обычно кросс-файловых вызовов и переходов меньше, чем глобальных переменных. Для этого графы вызовов учитывают только глобальные символы, предполагая, что исходные файлы должны быть модулями (что не всегда верно, но обычно они должны быть).

Чтобы сделать это, используйте свой инструмент для поиска между файлами, создайте большой список (например, в OpenOffice Calc), где вы собираете, какой символ определен в каком файле, и какие файлы ссылаются на этот символ, вызывающий его.

Затем украдите несколько больших (!) Листов из плоттера и начните рисовать. Если вы очень опытны в каком-либо графическом программном обеспечении, вы можете использовать его, но, если это не так, это с большей вероятностью вас сдержит. Поэтому нарисуйте график вызовов, показывающий, в каком файле есть вызовы, в какие другие файлы (без отображения самих символов, примерно с 50 файлами, вы не сможете управлять им).

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

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

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

Когда эти двое сделаны, «покой» довольно прост. Таким образом, вы должны знать, что должна делать каждая часть этой вещи, и, таким образом, вы знаете, с чем вы, вероятно, имеете дело, когда начинаете работать с исходным файлом. Однако важно, что всякий раз, когда вы обнаруживаете что-то «подозрительное» в источнике, программа, кажется, делает что-то неуместное, чтобы вернуться к вашей архитектуре и вызвать граф, и внести исправления, если необходимо.

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

1 голос
/ 12 июня 2009

Разрежьте на кусочки.

1 голос
/ 12 июня 2009

Это один из немногих случаев, когда я рекомендую вам применить ваши мягкие навыки и представить вашему PM / Manager / CXO свои аргументы в пользу переписывания, а также экономию времени / затрат, связанную с такими предприятие

1 голос
/ 12 июня 2009

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

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

...