Вопрос

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

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

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

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

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

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

Это было полезно?

Решение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Другие советы

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

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

Удачи

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

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

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

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

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

Удачи.

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

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

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

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

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

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

Не тратьте время на симулятор – с ним очень сложно работать и приходится делать много предположений относительно аппаратного обеспечения.Получите себе Внутрисхемный эмулятор (ICE) и запустить на оборудовании.

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

Может быть причина, по которой этот код беспорядок

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

ПРОСТРАНСТВО XDATA, ПРОСТРАНСТВО IDATA и ПРОСТРАНСТВО КОДА:

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

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

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

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

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

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

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

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

Так как же можно было начать это резать?

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

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

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

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

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

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

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

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

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

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

Я бы сказал, что ответ IanW (просто распечатайте его и продолжайте отслеживать), вероятно, лучший.Тем не менее, у меня есть немного необычная идея:

Попробуйте запустить код (вероятно, двоичный) через дизассемблер, который может восстановить код C (если вы сможете найти его для 8051).Возможно, он определит несколько процедур, которые вы не можете (легко).

Может быть, это поможет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top