среда, 26 ноября 2008 г.

Ловушка для меморилика

Не знаю как люди ловаят мемориликов. Пробовал google-perftools, что-то ничего не говорит даже на примитивный пример с единственным malloc. valgrind можно было бы попробовать прикрутить, но это связано с определенными трудностями, поскольку меморилик затаился в сервере c замкнутой программной средой на базе FreeBSD.

К ловле меморилика надо подходить творчески.

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

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

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

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

Некоторая проблема есть в том, что работать придется практически на системном уровне. Для реализации мы пишем свои версии malloc, free, realloc, strdup, ::operator new и других, оперирующие с памятью функций. в начале каждого обработчика определяем точку вызова. Но возможности вызвать функции из стандартных библиотек у нас уже нету.

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

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

Но на практике реализация всего этого встречает несколько проблем. О них в следующий раз.

четверг, 20 ноября 2008 г.

Отгадки времени

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

Появился баг - Невозможно создать сертификат с датами начала/окончания срока действия меньше/больше соответственно 1950/2049гг. Причем интерфейс позволяет ввести в качестве года значение от 1753 до 4111. И откуда взялись эти странные цифры?

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

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

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

Но текйщий момент времени мы можем получить только в time_t, диапазон которого ограничен датами от 1970 до 2037 года.

Но и generalizedTime сконвертировать в time_t сразу не получится. Можно воспользоваться структурой tm, диапазон которой снизу ограничен 1900 годом.

Сплошные ограничения. Могли бы создать проблемы, если бы текущее время системы можно было задвинуть за пределы 1970 года. Думаю что ни одна система этого сейчас не потерпит, а нам нужно только убедиться, что сертификат действует сейчас. Поэтому время сертификата можно смело обрубить до диапазона time_t, и сравнивать с текущим.

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

Ссылки по теме: RFC2459, RFC3161.

вторник, 18 ноября 2008 г.

Заниматься делом

У нас внедряется SCRUM. Сейчас идет третья двухнедельная итерация с момента внедрения. Можно сказать, что только начали ощущать вкус этого дела. До реальной пользы от гибких технологий нам еще далеко.

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

Но статья не про это.

Традиционный SCRUM, ежели таковой встречается в природе, подразумевает однородность комманд. То есть вся команда должна состоять из специалистов одного профиля. Например из одних программистов. Наши команды не такие. На треть они состоят из тестировщиков. Что вносит смуту в организацию работ. Наш таскборд состоит из 5 колонок: todo-coding-implemented-testing-done. Я бы конечно оставил три, но это вызывает проблемы с идентификацией работ по тестированию. Может быть выделять их цветом?

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

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

Видимо это происходит из привычной всем схемы работы над ошибками - ошибка может десятки раз ходить от тестировщика к разработчику, до тех пор, пока ее не исправят и не закроют. А это происходит из за того, что кто-то не ставит перед собой реальных задач. Гадание на кофейных гущах какое-то. Исправлять ошибку можно долго, можно заниматься этим годами. Но в условиях SCRUM надо научиться отвечать на вопрос - чем я собираюсь заниматься сегодня. Не так, что сегодня я мусолю ошибку номер xxxx, хрен знает что там такое. А стараться конкретизировать работу. Например: Чтобы исправить эту ошибку я прикручу такие-то возможности, и это позволит понять в чем дело.

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

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

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

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

вторник, 11 ноября 2008 г.

Компьютер для ребенка

Этим постом начну цикл статей про компьютер для ребенка. Последнее время как-то не до блога особенно, на работе вводится SCRUM, и времени практически нету. Но жизнь идет и мысли надо куда-то девать?

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

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

В качестве системы я естественно выбрал свою любимую Gentoo.

Не знал, что cdrom приводы Pioneer такие проблематичные, linux категорически отказывается воспринимать его как cdrom, без магического hdc=noprobe hdc=cdrom. Я несколько дней потратил в поисках нормально загружающегося и содержащего все необходимые инструменты linux'а, в конце концов обнаружил магическую комбинацию и вернулся к тому, с чего начал - с Gentoo install-x86-minimal-2008-r1.iso.

Забыл сказать, что детский компьютер у меня Pentium-MMX (молодой еще, всего 10 лет ему :D ). Видео они конечно смотреть на нем не смогут (если я видеокарту не поставлю какую нибудь более мощную), но аудио слушать смогут вполне. Да и десктоп должен вполне нормально крутится. При случае сделаю апгрейд.

Итак, чтобы накатить Gentoo на такой допотопный компьютер надо много времени... Но есть способ проще - Воспользуемся distcc. Сами portage делаем доступными с моего компьютера по NFS. Но есть еще проблема с компилятором. у меня i686-pc-linux-gnu, а там i486-pc-linux-gnu. Переводить его на i586 не вижу большого смысла, Я вообще не вижу особой разницы между i486, i586, i686, если кто знает о такой разнице - сообщите мне пожалуйста. Но для полноценной работы distcc необходимо, чтобы на моем родительском хосте стоял, кроме всего прочего, еще и i486-pc-linux-gnu.

Для этого устанавливаем crossdev, portage overlays должны быть настроены, и выполняем

# crossdev -S i486-pc-linux-gnu

Через некоторое время все необходимое устанавливается, но не совсем. Не знаю по каким причинам все установленные пакеты не включаются в world-файл и будут снесены при emerge --depclean. Для того чтобы этого избежать я добавил их в /var/lib/portage/world руками.

crossdev-i486-pc-linux-gnu/binutils
crossdev-i486-pc-linux-gnu/linux-headers
crossdev-i486-pc-linux-gnu/glibc
crossdev-i486-pc-linux-gnu/gcc

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

Например нету никакой необходимости интернационализировать консоль. как следствие keymaps и consolefonts тоже можно отключить.

# rc-update del keymaps
# rc-update del consolefonts

Также нет никакой необходимости в info, man и doc.

make.conf: FEATURES="noman nodoc noinfo"

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

Ссылки:
HOWTO: Portage через NFS (на данный момент недоступно, не знаю вернется ли вновь)
Описание distcc в Gentoo
DistCC Cross-compiling Guide

PS: Надо сказать что смотреть длинные видео через youtube не особо удобно. Смотрел тут про концепты по ссылке Юрия Волкова, на 20 минуте (смотрел я с долгими паузами) все заглючило и перестало воспроизводиться. Что сподвигло меня на поиск даунлоадеров, помню был какой-то консольный, найти не могу, зато нашел kde-misc/youtube-servicemenu, теперь можно сохранять ролики из контекстного меню konqueror.