Archive for the ‘code’ Category

Интегриране на GCC във Vim

Wednesday, March 25th, 2009

Вдъхновен от поста на един приятел, реших че може малко да си улесня живота като добавя бързи бутони за Compile и CompileRun във vim :) Пиниза наистина е лесен и си струва. Следните редове се добавят във .vimrc:


map <F9> :call CompileRun()<CR>
map <F5> :call Compile()<CR>
func! CompileRun()
        exec "w"        
        exec "!gcc % -o %< && ./%<"
endfunc
func! Compile()
        exec "w"
        exec "!gcc % -o %<"
endfunc

След това докато редактирате .с файл цъкате F5 и файла се компилира, F9 и се компилира и изпълнява. Евентуално може да бъде добавена и функцийка за make с Makefile.

Мониторинг качеството на нета

Wednesday, March 11th, 2009

Терминът “качество на нета” по-скоро е взет от някой тинейджърски журнал, но уцелва точно нуждите на крайния клиент. Аз като клиент на моя ISP се нуждая от две неща - ниска (по възможност - никаква) загуба на пакети и ниско (по възможност - никакво) забавяне на пакетите. Това трябва да ми се поднесе разбираемо, с картинка.
Нужен софтуер:

  • RRDtool - има страхотни възможности за представяне на числова информация. За примерни графики вижте галерията им.
  • ping - резултата от изпълнението на ping съдържа всичко необходимо за изграждане на статистиката ни - Round trip time и Packet loss
  • Perl - или друг удобен ви език. Трябва ви за парсване на изхода от ping
  • crontab - искаме статистиката да се събира през определен интервал от време

Системата може да бъде разделена на две части: част за събиране на статистика и част за графично представяне на данните.
Събиране на статистика
На всеки 5 минути ще се пуска ping до няколко избрани хоста в различни зони на пиъринга. Естествено върнатите резултати ще са субективни, защото зависят от параметрите на отсрещната точка. Все пак цялото задание “качество на нета” е субективно, така че да продължаваме :) Избрал съм единия хост да бъде шлюза на доставчика ми, втория хост да бъде dir.bg (тест на bg пиъринга) и третия хост да бъде ivanatora.info (за тест на международната свързаност).
Логът се изгражда от следния скрипт:
/home/ivanatora/perl_razni/ping_stats.pl


#!/usr/bin/perl
$target = $ARGV[0]; # тук ще идва адреса на целта
$db = $ARGV[1]; # тук ще идва в коя база ще се записва
$dump = "/home/ivanatora/stats_ping/$target.log"; # лога с крайните резултати
$rrd_path = "/home/ivanatora/rrd/temps/pings"; # директорията с RRD базите
open (FH, ">>", $dump) or die "$!";
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
$mon++;
$year += 1900;
$time = sprintf("%02d/%02d/%4d %02d:%02d:%02d", $mday, $mon, $year, $hour, $min, $sec);
$output = `/bin/ping -q -c100 -w100 $target`; # пуска ping със 100 пакета и чака максимално 100 секунди за изпълнението на командата

if ($output =~ /(d+?)% packet loss/){
        $loss = $1;
        $percent = sprintf("%3d", $1);
}
if ($output =~ /rtt min/avg/max/mdev = .+?/(.+?)//){
        $avg = $1;
        print FH "$time : $percent% loss to $target Average: $1 msn";
        # update RRD
        $ts = time;
        $mod = $ts % 300;
        $ts -= $mod;
        $avg =~ /^(.+?)./;
        $rtt = $1;
        `/home/ivanatora/rrd/bin/rrdtool update $rrd_path/$db.rrd $ts:$loss:$rtt`; # командата за ъпдейт на базата; TODO: да направя пътя до изпълнимия файл на rrdtool в променлива
}
close (FH);

Малко изпреварвам времето, защото още не сме създали RRD базите. Каква е идеята на RRD? Round Robin Database е способ за запазване на постоянно изменяща се информация във времето, като всяка промяна се регистрира в цикличен буфер. Ако имаме един буфер със 12 слота и ъпдейтваме базата през 5 минути, във всеки момент ще имаме данните от предния час. Какво става с по-старите данни - губят ли се? Да. Ако ни трябват ще направим още един буфер, който ще се ъпдейтва на всяко цялостно извъртане на часовия буфер - ще взима средната стойност на всички измервания и ще го записва в един негов слот. Така ако този буфер има 24 слота ще имаме статистика на средната часова стойност на измерванията. Може да имате колкото си искате вторични буфери и всички се съхраняват в една и съща база и се ъпдейтват автоматично. Освен това в един буфер може да пазите стойностите на няколко променливи. Предимствата на RRD е че всяка промяна се регистрира, като размера на базата не нараства линейно във времето.
До тук вероятно имаме лог с нещо такова:
/home/ivanatora/stats_ping/dir.bg.log

11/03/2009 00:05:01 :   0% loss to dir.bg Average: 7.271 ms
11/03/2009 00:10:01 :   0% loss to dir.bg Average: 9.091 ms
11/03/2009 00:15:01 :   0% loss to dir.bg Average: 7.466 ms
11/03/2009 00:20:01 :   1% loss to dir.bg Average: 7.152 ms
11/03/2009 00:25:01 :   0% loss to dir.bg Average: 8.273 ms
11/03/2009 00:30:01 :   0% loss to dir.bg Average: 6.863 ms
11/03/2009 00:35:01 :   0% loss to dir.bg Average: 11.690 ms

Този лог може да използваме за каквото си искаме, а може и въобще да не го правим. Горния скрипт работи ъпдейтва директно rrdtool, така че ако не искате излишни логове да се пазят на машината ви насочете $dump = “/dev/null”; :)

Създаване на RRD бази.
За всеки изследван хост ще направим по една база. В тази база ще се съхраняват стойностите за Round Trip Time и Packet Loss.
Правим си следното bash скриптче (освен ако не ви мързи да го пишете на ръка за всяка база - може да имате и повече от три бази):

#!/bin/bash
NOW=`date +%s`
rrdtool create $1.rrd --start $NOW --step 300 
DS:PacketLoss:GAUGE:300:0:100 
DS:RTT:GAUGE:300:0:10000 
RRA:LAST:0.5:1:288 
RRA:AVERAGE:0.5:6:336 
RRA:AVERAGE:0.5:24:360 
RRA:AVERAGE:0.5:288:365

Подробностите за синтаксиса можете да намерите в документацията на RRDtool. На кратко - създаваме база с 2 източника на данни (променливи, които ще следим): PacketLoss и RTT. Задават се интервалите на ъпдейт на всеки източник,минимални и максимални допустими стойности. След това се правят 4 Round Robin Archives (буферите, за които говорихме по-рано).
Създаваме трите бази:

[ivanatora@~/rrd/temps/pings]$ sh create_script.sh bg
[ivanatora@~/rrd/temps/pings]$ sh create_script.sh gw
[ivanatora@~/rrd/temps/pings]$ sh create_script.sh int

Слагаме cronjob за изпълнение на ъпдейтващия скрипт на 5 минути:

*/5 * * * * perl /home/ivanatora/perl_razni/ping_stats.pl 87.120.194.1 gw
*/5 * * * * perl /home/ivanatora/perl_razni/ping_stats.pl dir.bg bg
*/5 * * * * perl /home/ivanatora/perl_razni/ping_stats.pl ivanatora.info int

Както видяхме скрипта приема два аргумента: адрес за ping и име на базата, която ъпдейтва.
Рисуване на графиките
Ще имаме отделна графика за всяка следена величина. Може да комбинираме няколко от графиките на една, но само ако очаквате там приблизително еднакви резултати. Големите отклонения на величините карат rrdtool да измества разделителната способност на графиката и една от графиките в общата картинка не се вижда добре. Ще имаме два скрипта за чертане на графики - един за Round Trip Time и един за Packet Loss.
/home/ivanatora/rrd/temps/pings/loss_graph.sh

#!/bin/bash
PATH=$PATH:/home/ivanatora/rrd/bin
DIR=/home/ivanatora/rrd/temps/pings/
rrdtool graph $DIR/img/loss-$1.png 
--title="<$1> Packet loss" 
--color CANVAS#000000 
--color BACK#000000 
--color GRID#006600 
--color MGRID#003300 
--color AXIS#00ff00 
--color FONT#00ff00 
--color SHADEA#000000 
--color SHADEB#000000 
--slope-mode 
--upper-limit 100 
--rigid 
--width 480 
--height 300 
--font DEFAULT:10:Arial 
DEF:loss=$DIR/$1.rrd:PacketLoss:LAST 
DEF:rtt=$DIR/$1.rrd:RTT:LAST 
AREA:loss#00FF00:"Packet loss (%)" 

/home/ivanatora/rrd/temps/pings/rtt_graph.sh

#!/bin/bash
PATH=$PATH:/home/ivanatora/rrd/bin
DIR=/home/ivanatora/rrd/temps/pings/
rrdtool graph $DIR/img/rtt-$1.png 
--title="<$1> Round trip time" 
--color CANVAS#000000 
--color BACK#000000 
--color GRID#006600 
--color MGRID#003300 
--color AXIS#00ff00 
--color FONT#00ff00 
--color SHADEA#000000 
--color SHADEB#000000 
--slope-mode 
--width 480 
--height 300 
--font DEFAULT:10:Arial 
DEF:loss=$DIR/$1.rrd:PacketLoss:LAST 
DEF:rtt=$DIR/$1.rrd:RTT:LAST 
AREA:rtt#00FF00:"Round trip time (ms)" 

Тук можем да си поиграем с цветовете и крайния резултат да е доста eye-candy. Важните редове са DEF - където се извличат стойностите на променливите от базата и AREA - самото изчертаване. За повече подробности - документацията на RRDtool.
След това правим един скрипт, който да задейства изчертаване на всички графики:
/home/ivanatora/rrd/temps/pings/graph_all.sh

#!/bin/bash
DIR=/home/ivanatora/rrd/temps/pings/
$DIR/loss_graph.sh int
$DIR/loss_graph.sh bg
$DIR/loss_graph.sh gw
$DIR/rtt_graph.sh int
$DIR/rtt_graph.sh bg
$DIR/rtt_graph.sh gw

И поставяме съответната задача в crontab:

*/5 * * * * sh /home/ivanatora/rrd/temps/pings/graph_all.sh

В резултат всичките изображения се намират в директорията pings/img/. Остава да направим красива HTML страничка, която да събира всичко в едно.
Резултата можете да го видите тук.

Ах този фреймрейт…

Tuesday, February 3rd, 2009

Не ми харесва когато се окаже че субтитрите за някой филм не съвпадат с кадрите на филма. Резултата е постоянно избързване или изоставане на субтитрите спрямо действието, което с течение на времето се увеличава все повече и повече. Някой от умните съвременни плеъри имат опция за смяна на framerate на субтитърния файл. Но повечето го нямат.
По-неприятното е когато всички български сайтове за субтитри имат само една версия на субтитрите, които ви трябват, и то тази версия е за фреймрейт, който е различен от този на филма ви. Тогава или постоянно коригирате субтитрите (бутони Z и X в mplayer) или ги зарязвате и гледате филма в оригинал. Поздрави на хората, които могат от първо слушане да разбират говорим диалектен и жаргонен английски език. Завиждам им.
Алтернативата е да си оправите субтитрите - в нета има доста програмки, които нагласят фреймрейта. Всъщност цялата философия е да се смени времето на появяване на всяка реплика по една проста формула, за това реших да се поупражня и да си напиша моя програмка.
Представям ви - subconv.c. Програмата за сега работи само със субтитри във формат MicroDVD, а в кода има инструкции за експлоатация. Много неща трябва да се пипнат там, което ще направя когато отново ме стегне шапката че не мога да гледам нещо :)

Грижа за очите и душата

Wednesday, June 11th, 2008

През мен са минали доста сорсове, писани от различни хора. Целта на този пост не е да ви уча как да пишете професионално (къде дават дипломи за програмисти :) ), а да пишете разбираемо. Винаги пишете с идеята, че някой друг след вас ще трябва да ви променя кода. Още по-добре - пишете с идеята, че вие самия ще трябва да си променяте кода след месеци… и няма да разберете какво сте написал.
Защо това е важно? Ъпгрейди винаги се налагат - хората искат добавяне на функции. Бъгове винаги се получават, колкото и безгрешно да пишете. Условията на средата се променят, входните параметри се променят, и накрая нещо ще трябва да се фиксва.
Рано или късно всеки сорс минава през преразглеждане. Мислете за колегата, на когото ще му се наложи да се оправя из вашите сорсове. Не ви е направил нищо лошо - не му тровете нервите. Не го карайте да ви псува.

Ето няколко основни правила за писане на четим код.
- Научете български. Научете и английски. Нека програмата ви да е поне граматически правилна.
- Пишете коментари - по много. Знам че в момента на писането всичко ви е ясно и програмата ви е в главата. След седмица или месец няма да помните че сте писал съответния код.
- Пишете документация. Поне описание на файловете и на включените библиотеки. Ако използвате функции, които са описани в други файлове, добре е да пишете кое от къде идва.


....
include("../../../../sys.php");
include("../../../lib.php");
include("../../../../../pre.php');
....
check($alpha);

От къде е дошла check()? Какво прави? Идете и преровете горните файлове, които може да са по сто хиляди реда код, а може и да include()-ват още сто файла.
- Именувайте променливите и функциите според това какво правят. Използвайте кратки имена, не по-дълги от 3-4 думи. Използвайте долни черти между думите, или пишете думите заедно, но всяка следваща дума да започва с главна буква. Не използвайте едносрични имена - не се опитвайте да пишете ‘бързо’ - пишете ‘четимо’. Не използвайте имена с номерация - ако имате няколко променливи, които имат еднаква фукция, може би е по-добре да ги вкарате в масив?
Лоши наименования:

check() //какво се проверява?
dosomeprettyboringstuffandmore() //опитайте се да го напишете няколко пъти
test_directories_check_users_update_db_on_exist() //ако една функция трябва да прави много неща, може би е по-добре да я разделите на няколко функции
$var //ами вижда се че е променлива
$var1, $var2, $var3 //още по-зле

Добри наименования:

run_query()
$thumb_path

- В много езици можете да използвате return value от една функция, като го пуснете като параметър на следващата функция и така навържете верига, която ви дава краен резулатат. НЕ ГО ПРАВЕТЕ! Удобно е, бързо е, изкушаващо е… и е убиец на мозъчни клетки. Особено ако работите със ваши функции.

$tmp .= (int $result[$_]). " (" .(sprintf "%.2f", 100 * $result[$_]/$votes). "%) " . "$inquiry{answer}[$_]<br/>n" for(0..$#{$inquiry{answer}});

- В същата връзка - избягвайте да пишете if then else на един ред, както и оператори за цикли.

foreach (array_keys($outbond) as $urlname) if (preg_match($short_name_regex, $urlname, $m)) $output{""$shorturl" -> "{$m[1]}"} = true;
else if ($debug) print "CATCH :: $urlnamen";

Колкото и засукано да го напишете, няма да станете по-добър програмист, ако ще и сто оператора да наредите на един ред. Горното написано както трябва:

foreach (array_keys($outbond) as $urlname){
                if (preg_match($short_name_regex, $urlname, $m)){
                        $imeto = $m[1];
                        $output{""$shorturl" -> "$imeto""} = true; //wsichko se typche v hasha $output
                }
                else {
                        if ($debug) print "CATCH :: $urlnamen"; //neshto kato nepredvidena situaciq
                }
}

- Троичния оператор (ternary operator) е хитър и удобен, и както всичко хитро и удобно описано до тук - с него не трябва да се злоупотребява. Няма да ви заболи ако си го напишете с if-then-else.
Лош код:

while(<PSX>) { $_ =~ / (?)?(pts/0)?[ ]+w+[ ]+w+:w+ perl/ && !($_ =~ /bin/) && !($_ =~ /lost/) ? print "found! $`$'" : print "";
$_ =~ /lost/ ? print "found .. selfn" : print "";}

- Не редете дълги и много условия в един if. Ако все пак се налага - разбийте ги на редове. Например:

if (
                        !preg_match('!^/!', $one_link) &&
                        !preg_match("!^(http://)?$shorturl!", $one_link) &&
                        !preg_match("!^?!", $one_link) &&
                        !preg_match("!^#!", $one_link) &&
                        !preg_match("!^[wd_-]+?.php?!", $one_link) &&
                        !preg_match("!mailto!", $one_link) &&
                        preg_match("!^http://!", $one_link)
                ){.....


- Не смесвайте PHP/Perl + HTML + JavaScript + CSS !!! Няма нищо по-гадно от това да видиш променливи, напъхани измежду двойни, escape-нати кавички, цели блокове код затворени в if-switch блокове. Ако се налага да пишете уеб, особено ако става дума за нещо по-голямо от 2-3 странички - използвайте template система.

$tmp = "&lt;form action="$ENV{SCRIPT_NAME}" method="post"&gt;&lt;div&gt;
  &lt;label&gt;$inquiry{title}&lt;/label&gt;&lt;br/&gt;&lt;br/&gt;n";
  $tmp .= "&lt;input type="radio" name="answer" value="$_"/&gt; $inquiry{answer}[$_]&lt;br/&gt;n" for(0..$#{$inquiry{answer}});
  $tmp .= "&lt;br/&gt;&lt;input type="submit" name="button" value="Send"/&gt;&lt;/div&gt;n".
  "&lt;/form&gt;n";

Горните сорсове са писани както от мен така и от други хора. Предварително не се извинявам ако някой се засегне.

Карта на интернет

Tuesday, June 3rd, 2008

Нещо ме е хванала скуката. Пробвах да уча по Компютърни Архитектури, но резултата беше следобедна дрямка :)
Така че за да се измъкна за малко от творческата дупка, упражних малко PHP и регулярни изрази.
Резултата е налице - скрипт, който прави карта на ‘интернет’ :D
По-точно приема като параметър URL и тръгва по изходящите от там връзки. Изходяща е връзка към домейн различен от домейна на URL-то. Не се следят броя връзки, нито въпросното URL се crawl-ва до всяка под-страница. Така от всеки домейн се преглежда само по едно URL, което означава че картата е далееече от точност :) Ама я си представете от някой голям портал като dir.bg колко връзки навън има… и как се представят графично. За графичното представяне използвам Graphviz.
Вижда се че тук-таме субдомейните се представят като отделни сайтове. Примерно според скрипта idg.bg и www.idg.bg са две различни неща. Това е така, защото субдомейна www наистина може да е съвсем друг сайт :) Скрипта не проверява съдържанието буква по буква за съвпадение.
И естествено целия паяк има ‘ограда’, за да не тръгне да прави наистина карта на Интернет. Това което се вижда е на разстояние до 2 хопа от ivan.tu-plovdiv.info.
Извинявам се на сайтовете, които се виждат вътре за спамопободния трафик към тях.
Ето я й самата карта (не съм си играл с настройките за шарении):
out.jpg

А тук има същата карта, но този път се вижда малко по-далече - на 3 хопа разстояние. Всичко се върти около google, picasa, youtube, myspace… Хмм, виждат се и няколко бъга, които имат нужда оправяне :)