Распаковка языка языка спагетти

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

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

Удачи.

Ответ 3

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

Ответ 4

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

Ответ 5

Я делал это несколько раз. Некоторые рекомендации:

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

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

Удачи.

Ответ 6

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

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

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

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

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

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

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

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

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

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

ПРОСТРАНСТВО XDATA, ПРОСТРАНСТВО ИДЕТ И КОДЕКС:

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

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

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

Ответ 7

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

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

Ответ 8

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

Ответ 9

Разделите его на куски.

Ответ 10

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 11

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

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

Возможно, это поможет.