понедельник, сентября 24, 2007

Любите править чужие баги?

Никто не любит копаться в старом чужом коде, и вылавливать там баги (ну, почти никто :). Но порой приходится. Что делать, когда перед тобой система в сотню тысяч строк кода, задача buglog-а, причем и то и другое ты видишь в первый раз?
Большинство приложений, с которыми приходится иметь дело имеют похожую высокоуровневую структуру. Впереди располагается "морда" в виде GUI, web или windows, а на противоположенном конце БД (для .Net это чаще всего SQL сервер). Между этими полюсами располагаются десятки и сотни тысяч строк кода. Структура этого кода тебе не известна, а во взгляде начальника в место сочувствия читается "ну - ну, сейчас мы посмотрим, что ты за программер...". На этот случай у меня есть очень простая и эффективная техника локализации ошибки, о которой я хочу рассказать.

Сначала воспроизводим баг в тестовом environment-е, и локализуем в UI точку входа для воспроизведения бага. Что-то вроде "вот, если теперь нажать на кнопку "Details", система рушится в синий экран".

Вторым шагом, идем в backend, на SQL сервер и запускаем SQL profiler. Наша цель - перехватить все SQL команды отправляемые нашей системой в БД. Функциональная структура рассматриваемого нами класса систем, несмотря на все их разнообразие, довольно однородна: по команде с UI система выполняет ту или иную бизнес логику, которая неизбежно приводит к записи данных в БД. Итак, запустив SQL профилировщик, еще раз инициируем в UI сценарий воспроизводящий баг. После этого останавливаем профилировщик и анализируем полученный лог. Нас будут интересовать имена вызываемых хранимых процедур и SQL запросы. Эти имена мы ищем в коде системы. Они обязательно там будут, в виде строковых констант, в ресурсах или, быть может, в файлах конфигурации :). Наша цель, найти в коде точки, из которых вызываются хранимые процедуры и SQL запросы и установить в них точки прерывания.

После этого, мы в третий раз выполняем сценарий воспроизводящий баг, но уже в режиме отладки. Остановившись в точке прерывания, мы спешим увидеть стек вызовов (Ctrl+Alt+C в студии), потому что call stack, это и есть та нить Ариадны, которая проведет нас через сотни тысяч строк кода незнакомой системы. Конечно, если система имеет несколько физических уровней, то таким способом мы препарируем только один уровень. Впрочем, при должном умении, можно получить цепочку стека вызовов всех уровней системы, но тут уже многое зависит от конкретной архитектуры. Далее, расставляем точки прерывания по стеку вызовов и занимаемся исправлением бага.

четверг, сентября 20, 2007

Великая тайна программистов

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

В одном из них рассказывалось о "двунаправленной синхронизации каталогов Oracle Internet Directory и Microsoft Active Directory" для целей аутентификации, а также о Transparent Data Encryption (TDE) и Virtual Private Database (VPD) для обеспечения защиты хранимых данных, о построенном стенде и проведенных исследованиях. В другом речь шла о построении корпоративной системы на базе SOA, о публикации сервисов в реестре UDDI и о последующем поиске и подключении их заинтересованными системами-потребителями. Помню, оба документа произвели на меня в свое время сильное впечатление своим неукротимым полетом технологической мысли.

Имея возможность лицезреть готовые результаты по обоим проектам, я был поражен, насколько далеки оказались полученные результаты от запланированных архитектурных схем. В первом проекте вместо Oracle оказался MS SQL, а следовательно никакой "двунаправленной синхронизации каталогов" не понадобилось. Вместо TDE и VPD, используется симметричное шифрование общим ключом и обычное разграничение доступа к данным на уровне бизнес логики. Во втором проекте получилась обыкновенная трехзвенка, а единственный web сервис выдает неформатированный XML, и следовательно, для автоматического обнаружения и подключения никак не пригоден.

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

Так, а в чем же состоит "Великая тайна программистов"? Дело в том, что мы, программисты, любим всякие новые технологии и продвинутые архитектуры. Мы создаем их ежедневно в огромных количествах, подобно тому, как Мать Природа плодила биологические виды во времена Кембрийского эволюционного взрыва. Потом мы с большевистской напористостью продвигаем все эти новые фичи в наши проекты, руководствуясь порой одним лишь правилом: "Cool is a powerful reason to spend money" (Nathan Myhrvold). А тайна состоит в том, что за все это платит заказчик (пользователь). Но мы ему об этом никогда не скажем.

среда, сентября 19, 2007

DevDays Fall 07. Москва

Вчера посетил "Дни разработчика Осень 07". Далее отчет и впечатления.

Утренний экспресс из Дубны как обычно опоздал и поэтому начало первого доклада я пропустил. А первый доклад был про SQL Server 2008. Насколько я понял, революционных изменений в SQL server ждать не стоит, одна сплошная ползучая эволюция. Улучшение производительности, управление на основе политик, поддержка Entity & LINQ, а также масс мелких новых фич. Впрочем, некоторые из фич производят впечатление глубокой бетты, в частности, показанная Change Data Capture. Другие, хоть и невелики но довольно интересны. Например, поддержка оператора MERGE (update + insert в одном флаконе). Наконец-то появятся раздельные типы данных для даты и времени. Также среди новых типов - FileStream, позволяющий вовлекать в транзакционно-реляционный процесс файловые данные (видимо, это отголоски долгой и безуспешной борьбы за WinFS), HierarchyID - для поддержки иерархий, и какие-то забавные типы Geometry и Geography, которые впрочем, вовсе не SQL, а CLR типы. По поводу SSIS, SSAS и Reporting Services не было сказано ни слова (или я пропустил?).

Второй доклад посвященный WCF делал Марат Бакиров. Рассказывал он много и весело, но времени как всегда на все не хватило. Для тех, кто с WCF знаком в докладе не было практически ничего нового, а для тех, кто услышал о нем в первый раз много было не понятно. Из интересного. В Orcas появится шаблон Syndication, который будет предоставлять заготовку для сервиса в формате RSS и Atom, так что, не надо будет заморачиваться на деталях протокола, а просто накидывать контент в фид. Там-же (в Orcas) обещают поддержку Json и Ajax в сервисах WCF.

Третий доклад "Многопоточное программирование - вольный стиль" делал представитель Intel Василий Маланин. Доклад был посвящен замечательной C++ библиотеке для параллельного программирования Threading Building Blocks (TBB) и тому, как ее можно использовать в .Net. По этому поводу стоит заметить что Microsoft готовит свою параллельную библиотеку Parallel FX, которая конечно более тесно интегрирована с CLR. Но с другой стороны TBB выглядит несколько мощнее именно в плане многопоточности. Впрочем реально ни той ни другой библиотеки я пока не использовал и оценки делать наверное еще рановато.

Четвертый доклад делали представители AutoDesk. Оказывается семейство продуктов Autocad теперь может быть хостом для CLR и AutoDesk призывает всех разрабатывать для них plugin-ы на .Net. Впрочем тема заинтересовала не многих, и я не в их числе. Кому интересно читайте о докладе в блоге Vadim Horyakov

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

После обеда были доклады по WPF и ASP.NET, но на них я к сожалению не присутствовал.

понедельник, сентября 17, 2007

MSDN Mag October - Thinking Parallel

Вышел октябрьский номер MSDN Magazine на русском языке.
Большая часть номера посвящена параллельным вычислениям. Статьи "Параллельный LINQ: Выполнение запросов на многоядерных процессорах" и "Параллельная производительность: Оптимизация управляемого кода для многоядерных компьютеров" по сути анонсируют новую библиотеку Parallel FX, которая будет доступна во .Net Framework 3.5, постепенно погружают нас в мир понятий и проблем, связанных с параллельными вычислениями.

Названия других статей номера тоже говорят сами за себя: Роберт Сакконе, "Потоки в пуле: Улучшение масштабируемости с помощью новых API пула потоков"; Шон Вилдермут,
"Потоки WPF: Создание более быстро реагирующих приложений с Dispatcher"; Стивен Тауб, ".NET: вопросы и ответы: Монитор взаимоблокировок".

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

Параллельный LINQ и другие...

В своей статье "Программное обеспечение и параллельная революция" Херб Саттер (Herb Sutter) и Джэймс Лэрус (James Larus) отмечали, что современная программная инженерия стоит перед серьезным вызовом. Стремительно распространяется многоядерная архитектура вычислительных ресурсов, и адекватным ответом со стороны программного обеспечения может быть только повсеместное распространение параллельных вычислений.

И вот, результаты не заставили себя ждать. В октябрьском номере MSDN Magazine опубликована статья Джо Даффи и Эда Эссея "Параллельный LINQ: Выполнение запросов на многоядерных процессорах". По сути это анонс готовящегося к выходу пакета Parallel FX разработкой которого сейчас занимается Microsoft® Research и команда CLR.

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

четверг, сентября 13, 2007

С праздником!

Сегодня, 13 сентября - День программиста, который принято отмечать в 256 день года.
Долгое время не было единства, в том когда же все таки отмечать этот праздник. Нет его и сейчас. Некоторые предлагают 10 декабря, другие 19 июля. Но в последнее время большинство склоняется к этой дате.
Поздравляю всех тех, кто хоть раз в жизни заставлял оживать беспомощное электронное железо, подчиняясь написанной вами программе. Вы можете причислить себя к славному племени программистов, чьими незаметными стараниями так круто меняется вся сегодняшняя наша жизнь.

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

С праздником вас, коллеги!

вторник, сентября 11, 2007

Это не web сервисы...

Web сервисы завоевали свое место под солнцем в инфраструктуре приложений масштаба предприятия. Теперь среди разработчиков и пользователей таких систем часто можно слышать диалоги вроде: "Нам нужны сводные данные по продажам из вашей системы. У вас нет подходящего web сервиса?". И часто подходящий сервис находится.
К сожалению, эти web сервисы зачастую сделаны так, что назвать их не то что "подходящими", но и "web сервисами" просто не поворачивается язык.
Вот пример (я использую псевдокод для описания контракта сервиса вместо WSDL, чтобы было понятнее):



[WebMethod]
public XmlNode CT(DateTime dateFrom);

или

[WebMethod]
public string GetSales(string from, string to, string office);



Делать какие либо предположения, для чего предназначен метод CT() на основе его контракта абсолютно бесперспективное занятие. То же можно сказать и о выдаваемых им результатах. "Чтобы понять - надо жевать" - чтобы разобраться с результатами надо вызвать метод и вывести полученный XML на печать или экран, а затем читать его и думать что же делать дальше.
А вот, извините за выражение, стринговый контракт метода GetSales() по заверениям его разработчиков был разработан исключительно в целях кроссплатформенной совместимости. При этом, выяснились удивительные вещи. Оказалось что передавать даты можно только в формате "DD-MM-YY", любой другой приводит к падению сервиса. Результирующая строка содержит в себе довольно кучерявый XML в неизвестной кодировке, а при отсутствии продаж за период результат содержит (внимание!) строку вида: "There are not sales {имя офиса} for period".
Примеры подобного рода можно продолжать долго. Однако все это не web сервисы.
Факт, что Web сервисы на сегодня, это самый простой, быстрый и удобный способ организовать межпрограммное взаимодействие как на .NET так и на Java. И многие используют их именно как механизм удаленного вызова через SOAP over HTTP. И забывают при этом об основном отличие web сервисов от механизмов RPC - способности к само-описанию посредством метаданных представленных в WSDL.
WSDL описывает два важнейших аспекта сервиса: способ вызова методов (binding and ports) и формат передаваемых сообщений (message and types). В приведенных в качестве примера сервисах невозможно сказать, что либо определенное о формате их сообщений. Использование такого сервиса возможно только в комплекте с программистом его написавшим, а при недоступности последнего такие сервисы становятся совершенно бесполезны.
В общем, "сказка ложь, да в ней намек,/ добрым молодцам - урок". Урок прост:
- не используйте в сообщениях web сервисов XML не описанный в WSDL схеме сервиса;
- не используйте в сообщениях web сервисов строки вместо элементарных типов
- не используйте в сообщениях web сервисов строки для передачи произвольного XML;
- не используйте в web сервисах нетипизированные DataSet-ы;
- комментируйте контракты web сервисов, это как раз тот случай, когда комментариев много не бывает.

вторник, сентября 04, 2007

Секреты отладки

Отладка, самая сложная и пожалуй самая увлекательная часть работы программиста. Вы так не считаете? Тогда, вам стоит подумать о переходе в менеджеры.
К сожалению, премудростям отладки нигде не учат. Но к счастью есть люди, которые постигли этот путь в совершенстве. С одним из них - Андрем Бороздиным мне посчасливилось работать, а сегодня я рад представить вам его блог "Будни программиста" и интересный материал "Установка брейкпоинтов в управляемом коде без исходников" . Хороший материал. Так держать!

понедельник, сентября 03, 2007

Об опасности дефолтных значений

Программисты любят дефолтные значения. Они, собственно их и выдумали. Для программиста слово дефолт ласкает слух, у всех других оно вызывает самые неприятные ассоциации (дефолт рубля, большой дефолт 1998 года...). Но неприятности с дефолтами бывают и у программистов.
Жила была одна система. Среди прочего, она обрабатывала некие задачи. Задачи хранились в БД и были у этих задач состояния. В системе эти состояния были представлены перечислением (enum), а в БД, соответственно - числом (int):

public enum TaskState
{
//Ожидает
Waited,
//Назначена и исполняется
Assigned,
//Отклонена
Rejected,
//Завершена
Completed
}

public class Task
{
public TaskState State;
}


Как известно, в C# тип enum может базироваться на любом из базовых перечислимых типов, а по умолчанию для этого используется Int32. Если значения enum не указаны явно, то первому соответствует 0, второму 1 и т.д.
Но вот в один прекрасный день, в системе решили сделать улучшение - добавить возможность приостановки выполнения заданий. В перечисление добавили еще одно значение TaskState.Suspended, а в бизнес логику соответствующие методы:


public enum TaskState
{
//Ожидает
Waited,
//Назначена и исполняется
Assigned,
//Приостановлена
Suspended,
//Отклонена
Rejected,
//Завершена
Completed
}

public class Task
{
public TaskState State;
public void Suspend(){...}
public void Resume(){...}
}


Разрушительный эффект от этих нехитрых действий был потрясающий. В системе неожиданно возникла масса заданий в состоянии Rejected, а тысячи завершенных заданий (Completed) исчезли. Оно и понятно, если раньше состоянию Completed соответствовало число 3, то теперь оно соответствует состоянию Rejected. Самое смешное, что все модульные тесты прошли успешно.
Горячие головы уже спешно писали набросок скрипта для конвертации данных при переходе на новую версию, а всего то надо было явно объявить значения перечисления:

public enum TaskState
{
//Ожидает
Waited = 0,
//Назначена и исполняется
Assigned = 1,
//Приостановлена
Suspended = 4,
//Отклонена
Rejected = 2,
//Завершена
Completed = 3
}

Как вытащить иконку из ресурса

Недавно спрашивали у меня subj. Поскольку с формами я давненько не работал пришлось порыться в запасниках старого кода.
Итак, сначала, как внедрить иконку в ресурс сборки. Нет ничего проще. Добавляете файл иконки в проект Visual Studio. В свойствах файла меняете значение "Build action" на "Embeded resource". Все, при компиляции иконка будет добавлена как ресурс сборки.
Для того, чтобы извлечь иконку из ресурса используем метод Assembly.GetManifestResourceStream(string). Приведенный ниже код демонстрирует (а) как получить ссылку на сборку, (б) как извлечь иконку из ресурса сборки


namespace ResourceIcon
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void btnShowIcon_Click(object sender, EventArgs e)
{
Assembly assembly = Assembly.GetAssembly(typeof(Form1));
//Assembly assembly = this.GetType().Assembly;
using (Stream stream = assembly.GetManifestResourceStream("ResourceIcon.factory.ico"))
{
this.Icon = new Icon(stream);
}
}
}
}



Все очень просто. Сложности могут возникнуть с именем внедренного ресурса. Студия формирует имя ресурса так: берется Default Namespace, указанный в свойствах проекта и через точку добавляется имя файла. В крайнем случае, вы можете скомпилировать проект и затем открыть полученный .exe или .dll файл в ildasm.exe и посмотреть в манифесте сборки имя нужного вам ресурса.