tag:blogger.com,1999:blog-308095942018-08-31T00:51:12.272+03:00Псевдоэкспертные заметки технаряЗаметки о программировании, стандартах, методиках и т.п.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.comBlogger60125tag:blogger.com,1999:blog-30809594.post-21027410147513969822013-09-12T10:22:00.000+04:002013-09-12T10:22:20.747+04:00Почему не стоит привязываться к устаревшей JavaПрочитал замечательную <a href="http://arstechnica.com/security/2013/09/security-of-java-takes-a-dangerous-turn-for-the-worse-experts-say/">статью</a> про то почему опасно оставаться на Java 6. Вкратце мораль такова:<br /><br /><ol><li>Oracle прекратил поддержку Java 6 в феврале этого года, т.е. никаких официальных патчей можно не ждать;</li><li>Oracle регулярно выпускает security патчи для Java 7;</li><li>Люди в черных шляпах делают реверс-инжиниринг этих патчей и атакуют машины на которых крутится Java 6, для которых этих патчей не будет;</li><li>Машин на которых крутится Java 6 примерно 50% от всех машин с Java.</li></ol><div>Ну и кроме этого там есть ссылка на статью показывающую что <a href="http://blog.trendmicro.com/trendlabs-security-intelligence/java-native-layer-exploits-going-up/">всё больше эксплойтов создается для Java native layer</a> независимо от версии Java.</div><div><br /></div><div>В общем конечно удивительного мало, учитывая популярность Java и любовь финансового сектора к решениям на Java. Но тут прям обострение.</div>Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com1tag:blogger.com,1999:blog-30809594.post-14176546968208293972013-08-06T08:05:00.000+04:002013-08-06T08:05:00.821+04:00Лучшая фича C++ в CЛучшая фича C++ - деструктор. Точно не помню кто это сказал, по-моему Страуструп. Если задуматься это действительно так - если убрать деструкторы, то смысл использовать C++ пропадает, так как практически всё в C++ что отличает его от C построено на деструкторах (ну и на исключениях наверное, хотя некоторые лишены возможности их использовать в приказном порядке).<br /><br />Например в нижеприведенном коде главное не лямбда с кастомным deleter'ом, можно ведь сделать отдельный класс для счетчика и в деструкторе сделать то же самое что в лямбде (ну кроме delete) - лямбда, unique_ptr с deleter'ом всё это не критично для идеи(хотя конечно их наличие создаёт удобства), а вот если будет отсутствовать деструктор, то придется писать в C-стиле и возникнет вопрос "а зачем нам вообще C++".<br /><br />Disclaimer: не ищите в коде особый смысл, просто можете рассматривать как заготовку для локального дебаг логгинга - не важно где и как(через return или через исключение) мы выйдем из функции, но в лог запишется состояние счетчика на момент выхода(понятно что в реальности можно писать дамп объекта например, а не счетчика):<br /><script src="https://gist.github.com/Dmitriy403/6159363.js"></script> Так вот оказывается в C в gcc есть нестандартное расширение для эмуляции деструктора. Расширение выполнено в виде атрибута <a href="http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html">cleanup</a>. Ниже листинг кода на C аналогичный C++ выше, ну правда я слегка всё упростил и исключений нет (я не стал их эмулировать через setjmp()/longjmp(), так как пост не об этом) - но идея та же.<br /><script src="https://gist.github.com/Dmitriy403/6160404.js"></script>Не очень красиво, но красота в C наводится с помощью макросов, поэтому это исправимо. Так что к термину "C++ без исключений" можно добавить "C(си) с деструкторами".<br /><br />Кстати что меня удивляет это то что в C++ нет <a href="http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html">designated initializer</a>, хотя в C он есть с прошлого века. При работе с C-библиотеками иногда очень не хватает... Наверное есть весомые причины почему его нет в C++, но лезть копаться в стандартах и парсерах чтобы понять конкретную проблему я пока не готов. :) Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-58896116541823924172013-04-16T10:08:00.001+04:002013-09-26T13:31:20.871+04:00Воскрешение логов из дампа памяти (core dump)<i>Вся заметка про специфику под *nix подобными операционками. Но идея вобщем будет такой же и под Windows - инструментарий другой.</i><br /><br />Большинство логгеров делают буферизацию - т.е. пишут в буфер сообщения, а потом пачкой записывают их на диск (или шлют по сети). Ну даже те кто не делают это сами - как минимум не мешают операционной системе позаботиться хоть о какой-то буферизации. Это конечно хорошо для производительности, но плохо когда программа вылетает и в логах нет последних - самых интересных сообщений. Во многих логгерах можно просто отключить буферизацию, и если вы не пишете десятки тысяч сообщений в секунду, то вобщем всё будет относительно терпимо(хотя думаю даже на паре тысяч сообщений в секунду будут большие проблемы без буфера).<br /><br />Конечно можно воткнуть обработчик сигналов(signal handler) и прикрутив его попробовать форсировать сброс буфера(если логгер в принципе имеет такую возможность) на диск перед смертью программы. Проблема в том что если уже случилось что-то ужасное мы уже не можем быть уверены что логгер находится в рабочем состоянии на момент как мы получили сигнал и попали в обработчик.<br /><br />Если при этом у вас многопоточное приложение вам придётся пробежаться по всем потокам(кроме одного который и обрабатывает сигналы) чтобы заблокировать в них обработку сигналов в старом добром C стиле (ну я по крайней мере не видел аналога для pthread_sigmask даже в стандарте C++11). Да и для C++11 политика "не пишите поточных async'ов не заблокировав им сигналы" мягко говоря вряд ли будет популярной особенно если вы используете std::async с std::launch::async | std::launch::deferred и не знаете будет тут отдельный поток или нет. И если какой-нибудь поток пропустим и в итоге обработчик сигнала вызовется хрен знает в каком потоке и в худшем для нас случае(например если вы используете сторонний асинхронный логгер и вообще не можете контролировать создание им потока - то и заблокировать сигналы для него скорее всего не сможете) он вызовется в том же потоке что и логгер в момент когда логгер сделал lock перед записью - так что если мы в обработчике вызовем flush для логгера, то получим deadlock в большинстве логгеров(теоретически рекурсивный mutex тут поможет, но во первых не факт что он безопасен в случае async signal, да и нам это не поможет если логгер в виде сторонней либы). Вобщем чтобы не уползти в обсуждение сигналов страниц на 5, примем за аксиому - обработчик сигналов должен быть простым и тупым, например записать бэктрейс в stderr и умереть.<br /><br />Т.е. всё просто, если уж нам суждено умереть от чего-то ужасного(segfault и тп) - умираем не пытаясь шаманить с логгером. А потом лезем с помощью gdb в core dump и пытаемся восстановить недописанные логи.<br /><br />Для начала проверьте что core dump у вас разрешён:<br /><span style="font-family: Courier New, Courier, monospace;">ulimit -c</span><br /><span style="font-family: Courier New, Courier, monospace;"><br /></span>Если команда выводит 0, то установите unlimited:<br /><span style="font-family: Courier New, Courier, monospace;">ulimit -c unlimited</span><br /><br />Теперь напишем небольшую программу(не вздумайте использовать этот логгер как образец для подражания - код хорош только для наших исследовательских целей, за появление чего-то похожего в продакшене положен расстрел на месте) с псевдологгером с буфером и уроним её с core dump'ом. В классе мы запишем в начало буфера более-менее уникальную строку чтобы потом найти с помощью неё начало буфера в gdb. В случае использования не рукописного буфера придётся записывать уникальную строку в сообщение. В разных логгерах это можно сделать по разному - либо через кастомный форматтер, либо через контекстный дескриптор и тп. <br /><pre style="background: #f6f8ff; color: #000020;"><span style="color: #3f7f8f;">/**</span><br /><span style="color: #3f7f8f;"> * Run logging and then make core dump with not flushed log messages.</span><br /><span style="color: #3f7f8f;">*/</span><br /><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">iostream</span><span style="color: maroon;">></span><br /><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">string</span><span style="color: maroon;">></span><br /><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">sstream</span><span style="color: maroon;">></span><br /><span style="color: #004a43;">#</span><span style="color: #004a43;">include </span><span style="color: maroon;"><</span><span style="color: #40015a;">cstdlib</span><span style="color: maroon;">></span><br /><br /><span style="color: #200080; font-weight: bold;">using</span> <span style="color: #200080; font-weight: bold;">namespace</span> <span style="color: #0066ee;">std</span><span style="color: #406080;">;</span><br /><br /><br /><span style="color: #595979;">//! Пишем в буфер и флашим когда размер буфера > flushSize_</span><br /><span style="color: #200080; font-weight: bold;">class</span> PseudoLogger <span style="color: #406080;">{</span><br /><span style="color: #e34adc;"> </span><span style="color: #200080; font-weight: bold;">private</span><span style="color: #e34adc;">:</span><br /> <span style="color: #003060;">string</span> buffer_<span style="color: #406080;">;</span><br /> <span style="color: #200080; font-weight: bold;">const</span> <span style="color: #003060;">string</span> cookie_<span style="color: #406080;">;</span><br /> <span style="color: #003060;">size_t</span> flushSize_ <span style="color: #308080;">=</span> <span style="color: #008c00;">50</span><span style="color: #006600;">u</span><span style="color: #406080;">;</span><br /><br /> PseudoLogger<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">:</span>cookie_<span style="color: #308080;">(</span><span style="color: maroon;">"</span><span style="color: #1060b6;">my cookie</span><span style="color: maroon;">"</span><span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> resetBuffer<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><br /> <span style="color: #200080; font-weight: bold;">void</span> resetBuffer<span style="color: #308080;">(</span><span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> buffer_ <span style="color: #308080;">=</span> cookie_<span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><br /> <span style="color: #200080; font-weight: bold;">void</span> flush<span style="color: #308080;">(</span><span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> <span style="color: #003060;">cout</span> <span style="color: #308080;"><</span><span style="color: #308080;"><</span> buffer_<span style="color: #308080;">.</span>substr<span style="color: #308080;">(</span>cookie_<span style="color: #308080;">.</span>size<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /> resetBuffer<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><span style="color: #e34adc;"> </span><span style="color: #200080; font-weight: bold;">public</span><span style="color: #e34adc;">:</span><br /> <span style="color: #308080;">~</span>PseudoLogger<span style="color: #308080;">(</span><span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> flush<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><br /> <span style="color: #200080; font-weight: bold;">static</span> PseudoLogger<span style="color: #308080;">&</span> getInstance<span style="color: #308080;">(</span><span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> <span style="color: #200080; font-weight: bold;">static</span> PseudoLogger logger<span style="color: #406080;">;</span><br /> <span style="color: #200080; font-weight: bold;">return</span> logger<span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><br /> <span style="color: #200080; font-weight: bold;">void</span> Log<span style="color: #308080;">(</span><span style="color: #003060;">string</span> message<span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> <span style="color: #200080; font-weight: bold;">if</span> <span style="color: #308080;">(</span>message<span style="color: #308080;">.</span>size<span style="color: #308080;">(</span><span style="color: #308080;">)</span> <span style="color: #308080;">></span> flushSize_<span style="color: #308080;">)</span><br /> message<span style="color: #308080;">.</span>resize<span style="color: #308080;">(</span>flushSize_<span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /><br /> buffer_ <span style="color: #308080;">+</span><span style="color: #308080;">=</span> message<span style="color: #406080;">;</span><br /> buffer_ <span style="color: #308080;">+</span><span style="color: #308080;">=</span> <span style="color: #003060;">string</span><span style="color: #308080;">(</span><span style="color: maroon;">"</span><span style="color: #0f69ff;">\n</span><span style="color: maroon;">"</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /><br /> <span style="color: #200080; font-weight: bold;">if</span> <span style="color: #308080;">(</span>buffer_<span style="color: #308080;">.</span>size<span style="color: #308080;">(</span><span style="color: #308080;">)</span> <span style="color: #308080;">></span> flushSize_<span style="color: #308080;">)</span><br /> flush<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><span style="color: #406080;">}</span><span style="color: #406080;">;</span><br /><br /><br /><span style="color: #200080; font-weight: bold;">int</span> <span style="color: #400000;">main</span><span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">int</span> argc<span style="color: #308080;">,</span> <span style="color: #200080; font-weight: bold;">char</span><span style="color: #308080;">*</span><span style="color: #308080;">*</span> argv<span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> PseudoLogger<span style="color: #308080;">&</span> logger <span style="color: #308080;">=</span> PseudoLogger<span style="color: #406080;">::</span>getInstance<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /><br /> <span style="color: #200080; font-weight: bold;">for</span> <span style="color: #308080;">(</span><span style="color: #200080; font-weight: bold;">int</span> i<span style="color: #308080;">=</span><span style="color: #008c00;">1</span><span style="color: #406080;">;</span> i<span style="color: #308080;"><</span><span style="color: #008c00;">20</span><span style="color: #406080;">;</span> <span style="color: #308080;">+</span><span style="color: #308080;">+</span>i<span style="color: #308080;">)</span> <span style="color: #406080;">{</span><br /> <span style="color: #0066ee;">std</span><span style="color: #406080;">::</span><span style="color: #003060;">stringstream</span> out<span style="color: #406080;">;</span><br /> out <span style="color: #308080;"><</span><span style="color: #308080;"><</span> i<span style="color: #406080;">;</span><br /> logger<span style="color: #308080;">.</span>Log<span style="color: #308080;">(</span><span style="color: #003060;">string</span><span style="color: #308080;">(</span><span style="color: maroon;">"</span><span style="color: #1060b6;">test message </span><span style="color: maroon;">"</span><span style="color: #308080;">)</span> <span style="color: #308080;">+</span> out<span style="color: #308080;">.</span>str<span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /> <span style="color: #406080;">}</span><br /><br /> <span style="color: #003060;">abort</span><span style="color: #308080;">(</span><span style="color: #308080;">)</span><span style="color: #406080;">;</span><br /><br /> <span style="color: #200080; font-weight: bold;">return</span> <span style="color: #008c00;">0</span><span style="color: #406080;">;</span><br /><span style="color: #406080;">}</span><br /></pre><a href="http://ideone.com/ZQ6YWj">посмотреть на ideone</a><br />После запуска программа выдаст:<br /><span style="font-family: Courier New, Courier, monospace;">test message 1 </span><br /><span style="font-family: Courier New, Courier, monospace;">test message 2 </span><br /><span style="font-family: Courier New, Courier, monospace;">test message 3 </span><br /><span style="font-family: Courier New, Courier, monospace;">... </span><br /><span style="font-family: Courier New, Courier, monospace;">test message 16 </span><br /><span style="font-family: Courier New, Courier, monospace;">test message 17 </span><br /><span style="font-family: Courier New, Courier, monospace;">test message 18 </span><br /><span style="font-family: Courier New, Courier, monospace;">Aborted (core dumped) </span><br /><br />Т.е. логгер успел вывести все сообщения кроме последнего "test message 19".<br /><br />Теперь запустим<br /><span style="font-family: Courier New, Courier, monospace;">gdb program.out core</span> <br /><span style="font-family: Courier New, Courier, monospace;"><br /></span>В gdb запускаем<br /><span style="font-family: Courier New, Courier, monospace;">maintenance info sections</span><br /><br />Там нас интересует раздел для core файла (перед ним будет раздел exec файла), мы увидим что-то вроде этого(понятно что всё что ниже зависит от ОС, компилятора и флагов компиляции):<br /><br /><pre style="background: #f6f8ff; color: #000020;">Core file<span style="color: #308080;">:</span><br /><br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x0944</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00000394</span><span style="color: #308080;">:</span> note0 READONLY HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x0044</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x000003f0</span><span style="color: #308080;">:</span> .reg<span style="color: #308080;">/</span><span style="color: #008c00;">4230</span> HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x0044</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x000003f0</span><span style="color: #308080;">:</span> .reg HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x00a0</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00000570</span><span style="color: #308080;">:</span> .auxv HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x006c</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00000a14</span><span style="color: #308080;">:</span> .reg2<span style="color: #308080;">/</span><span style="color: #008c00;">4230</span> HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x006c</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00000a14</span><span style="color: #308080;">:</span> .reg2 HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x0200</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00000a94</span><span style="color: #308080;">:</span> .reg<span style="color: #308080;">-</span>xfp<span style="color: #308080;">/</span><span style="color: #008c00;">4230</span> HAS_CONTENTS<br /> <span style="color: green;">0x0000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x0200</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00000a94</span><span style="color: #308080;">:</span> .reg<span style="color: #308080;">-</span>xfp HAS_CONTENTS<br /> <span style="color: green;">0x8048000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x8048000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00001000</span><span style="color: #308080;">:</span> load1 ALLOC READONLY <span style="color: #004a43;">CODE</span><br /> <span style="color: green;">0x804a000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x804b000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00001000</span><span style="color: #308080;">:</span> load2 ALLOC LOAD READONLY HAS_CONTENTS<br /> <span style="color: green;">0x804b000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x804c000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00002000</span><span style="color: #308080;">:</span> load3 ALLOC LOAD HAS_CONTENTS<br /> <span style="color: green;">0x8623000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0x8644000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00003000</span><span style="color: #308080;">:</span> load4 ALLOC LOAD HAS_CONTENTS<br /> <span style="color: green;">0xb74b4000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0xb74b6000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00024000</span><span style="color: #308080;">:</span> load5 ALLOC LOAD HAS_CONTENTS<br /> <span style="color: green;">0xb74b6000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0xb74b6000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00026000</span><span style="color: #308080;">:</span> load6 ALLOC READONLY <span style="color: #004a43;">CODE</span><br /> <span style="color: green;">0xb74f7000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0xb74f8000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00026000</span><span style="color: #308080;">:</span> load7 ALLOC LOAD READONLY HAS_CONTENTS<br /> <span style="color: green;">0xb74f8000</span><span style="color: #308080;">-</span><span style="color: #308080;">></span><span style="color: green;">0xb74f9000</span> <span style="color: #004a43;">at</span> <span style="color: green;">0x00027000</span><span style="color: #308080;">:</span> load8 ALLOC LOAD HAS_CONTENTS<br /></pre><a href="http://ideone.com/qUpOZx">посмотреть на ideone</a><br />Нас интересуют сегменты load1, load2 и тп(на самом деле как раз конкретно load1 и load2 не интересуют, но вобщем нас интересует подмножество load сегментов). Попробуем поискать в них, но не всё что мы найдём будет тем что надо, потому что последовательность символов "my cookie" должна встречаться ещё как минимум один раз, но в реальности в зависимости реализации string, ключей компиляции и тп оно может встретиться ещё много раз. Поэтому мы после каждого найденного случая будем смотреть на этот кусок памяти, и если после "my cookie" идет нуль-терминатор, то это не то что нам нужно - нам надо чтобы сразу после "my cookie" шло что-то похожее на лог-сообщение (в нашем случае мы вообще-то знаем что это будет, но в реальности конечно нет). В нашем случае из-за убогой реализации и отсутствия оптимизации мы найдём порядочно совпадений. Скажу только что в реальности, с асинхронным логерром где буфер сделан не в виде std::string, а через хардкорный malloc и буфера тасуются между фронтэндом и бэкэндом вы тоже можете найти немало совпадений и потратить время чтобы вытащить то что вам надо. Итак ищем так (передаём "my cookie" посимвольно, потому что иначе find будет искать строку с нуль терминатором, а нам это как раз не надо):<br /><br /><script src="https://gist.github.com/Dmitriy403/6704275.js"></script><a href="http://ideone.com/JiK3Ds">посмотреть на ideone</a><br />Вобщем то что нам надо лежит по адресу 0x86232fc а по адресу 0x8623344 последний буфер который мы послали в cout. Как бы мы поняли в <b>реальности</b>(здесь то мы знали) что нам нужно то что в 0x86232fc а не в 0x8623344? Для этого в логи и пишут точное время для каждого сообщения - мы бы выбрали кусок памяти в котором сообщения моложе самого последнего сообщения записанного в лог-файл.<br /><br />Многие из вас заметят вполне логично, что нафиг тут gdb если можно взять core файл и в каком-нибудь редакторе в нём поискать нашу метку. Правда в gdb мы видим что в некоторых сегментах можно было и не искать - например очевидно что в READONLY load2 не лежит наш буфер. Ну и если вы пишете свой логгер то проще в начало буффера записать не текст, а что-то более компактное и относительно уникальное - например адрес какого-нибудь метода из того же класса логгера. Тогда в gdb вы можете получить этот адрес (чтобы потом искать уже его) с помощью такой команды:<br /><span style="font-family: Courier New, Courier, monospace;">(gdb) info address PseudoLogger::PseudoLogger</span><br /><br />Опять же удобно бэктрейс посмотреть, да много чего можно. Но дело не в инструменте - дело в принципе.<br /><br />P. S. Тут я привёл листинги из gdb после сборки на 32битной системе, просто чтобы в ширину было всё поуже и попроще читать. Кроме длины адресов разницы c 64битной системой в нашем случае никакой.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-2363640973466269612013-03-22T15:08:00.000+04:002013-03-22T22:56:20.681+04:00Логирование в С++. Бенчмаркинг Poco::Logger и google-glog.С <a href="http://pseudo-tech-notes.blogspot.ru/2011/09/blog-post.html">прошлого поста про логгирование </a>прошло порядочно времени. Я по прежнему считаю что <a href="https://code.google.com/p/google-glog/">glog </a>хорош, но очевидно не идеален. Не всем нравится, да и иметь альтернативу всегда приятно.<br /><br />Так вот решил я для мультиплатформенного кода попробовать Poco::Logger (ищите тут <a href="http://pocoproject.org/">http://pocoproject.org/</a> ). Вроде бы кто-то хвалил, даже у меня коммент к прошлой записи был про него, но я его случайно стёр в борьбе с очередной спам-атакой (кто бы его не писал - сорри, не хотел). Сначала показалось что поднакрутили лишнего в нем, но гибкость вполне на уровне и можно использовать так как тебе нужно. Можно использовать printf-подобные макросы или вызов метода класса или даже использовать Poco::LogStream чтобы писать как в ostream. После того как он мне более-менее понравился захотелось померять насколько он медленный по сравнению с glog.<br /><br />В итоге написал маленький бенчмарк в котором логгеры последовательно вызывали микробенчмарки в каждом из которых писали 1 сообщение с debug флагом и 2 обычных. Сообщение с debug флагом пишется естественно только в debug сборке, а в release в идеале пропадает без потерь в производительности. Лог пишем в один файл (те glog писал всё в INFO), хотя glog и пишет лог в разные файлы но в реальности под сильной нагрузкой на логгер львиная часть пишется в один файл (ну кроме редких пиков когда что-то совсем ужасное происходит, но в хорошей программе эти пики должны быть кратковременны). Также для каждого логгера есть один микробенчмарк в котором пишутся только debug сообщения - чтобы посмотреть насколько хорошо они испаряются в релизе.<br /><br />Результаты надо сказать <b>очень</b> сильно зависят от железа и состояния системы. Поэтому я их приводить не буду - лучше выложу исходники на <a href="https://github.com/Dmitriy403/logBenchmark">гитхаб</a>, кому надо - собирайте и меряйте.<br /><br />Но некоторые консистентные результаты я всё-таки опишу:<br /><br /><ul><li> в release сборке микробенчмарк с только debug сообщениями съедает практически абсолютный ноль и в случае с glog и в случае с Poco::Logger, так что можете писать debug логи хоть через строчку не боясь;</li><li>добавление FormattingChannel к логгеру Poco понятно просаживает производительность ощутимо, с другой стороны логировать без timestamp'а или ставить его периодически руками это нужно иметь очень сильные причины для этого;</li><li>если вы ошиблись и передали например в <span style="font-family: Trebuchet MS, sans-serif;">poco_error_f1(logger_, "bad thing: %s", <i>param</i>);</span><span style="font-family: inherit;"> в качестве параметра не строку а например int то на том месте где должен был быть подставлен этот параметр в лог пишется [ERRFMT]. Так вот в этом случае тоже происходит ощутимая потеря производительности - так что лучше быть внимательным. Хотя не заметить это в логах наверное сложно.</span></li></ul>В интересующей меня конфигурации glog был быстрее, но не настолько чтобы не рассматривать Poco::Logger как альтернативу. Опять же не забываем что условия слегка отклонены от реальных(glog пишет только в INFO) так что glog в реальности будет чуть медленнее чем в этом бенчмарке и они станут ещё ближе. Учитывая что у Poco есть AsyncChannel и вообще архитектура гибкая и расширяемая, то во многих случаях можно закрыть глаза на небольшие(в масштабах реальной программы, а не только логгера) потери производительности.<br /><br /> P.S. По поводу бенчмарка <a href="https://github.com/Dmitriy403/logBenchmark">https://github.com/Dmitriy403/logBenchmark</a> . <strike>FindGlog.cmake написан только под linux, так что кто хочет собрать под винду либо допилите его сами либо загрузите либу glog'а вручную как-нибудь так:</strike><br /><span style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4;"><strike>add_library(xxx UNKNOWN IMPORTED)</strike></span><br /><pre style="background-color: white; color: #333333; font-family: 'Bitstream Vera Sans Mono', 'DejaVu Sans Mono', Monaco, monospace; font-size: 12px; line-height: 1.4;"><strike>set_target_properties(xxx PROPERTIES IMPORTED_LOCATION "xxx.lib")</strike></pre><strike>Ну или соберите отдельно руками в чём вам угодно.7 файлов организовать и добавить две либы - не велика задача.</strike><br /><strike>P.P.S. </strike>Компилятор должен быть не очень древний, потому что я использовал пару С++11 конструкций. Но я думаю что они уже несколько лет в разных компиляторах есть (chrono и std::bind).Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-60478360117692659022011-09-23T18:01:00.000+04:002013-03-22T15:45:58.425+04:00Логирование в С++. Сравнительный мини-обзорВполне стандартная задача(логирование) имеет слишком много инструментов для решения в C++. В том плане что ярко выраженного лидера нет(ну если не считать за лидера "свой велосипед"). Пришлось мне ковыряться. Дело усугубилось тем что среди c++ девелоперов очень много людей считающих что подробная документация не нужна (кому надо - в исходник заглянут).<br /><div><br /></div><div>После поверхностного поиска я выделил следующее:</div><div>log4j клоны для c++ - <a href="http://logging.apache.org/log4cxx/">log4cxx</a> , <a href="http://log4cpp.sourceforge.net/">log4cpp</a> , <a href="http://log4cplus.sourceforge.net/">log4cplus</a>;</div><div><a href="http://www.pantheios.org/">Pantheios</a>;<br /><a href="http://boost-log.sourceforge.net/">boost-log</a>;<br /><a href="http://code.google.com/p/google-glog/">google-glog</a>.<br /><br />Попробовав log4cxx я заподозрил плохое увидев что по сути в проекте не было изменений с 2004 года. Увидев незакрытые баги связанные с многопоточностью(что для меня критично), я на него забил и решил пока не трогать другие log4j клоны (судя по датам релизов живой только log4cplus и если вам хочется во чтобы-то ни стало использовать клон log4j то наверное стоит обратить внимание именно на него).<br /><br /><i>Далее идут не клоны log4j, так что стандартная схема log4j включая все плюсы и минусы в них отсутствует. Это не значит что они слабее функционально (хотя некоторые объективно нацелены на простоту), они просто другие.</i><br /><br />Pantheios выглядит отлично в плане документации/описаний, по отзывам вроде у всех работает без проблем. Я почти остановился на нём, но решил уж посмотреть оставшиеся альтернативы.<br /><br />Boost-log также неплохо документирован, но не включен официально в boost и есть сообщения о странностях в многопоточных приложениях. Вобщем для использования нужны дополнительные аргументы в его пользу после дополнительных исследований.<br /><br />Наконец очередь дошла до google-glog. И тут я понял что это то что мне нужно. Очень мощный и одновременно простой функционал. DLOG (ну и DCHECK и тд) например позволяет выкинуть мысли о том во что выльется слишком подробное логирование в плане производительности - как только вы скомпиляете релиз-версию все DLOG вызовы <strike>превратятся в тыкву</strike> просто исчезнут. Но у glog есть и минусы. Например встроенной ротации логов нет. Документация очень куцая, часто приходится заглядывать в исходники (особенно в <a href="http://code.google.com/p/google-glog/source/browse/trunk/src/logging.cc">http://code.google.com/p/google-glog/source/browse/trunk/src/logging.cc</a> ) для поиска настроек (например для максимального размера логфайла).<br /><br /><b>Вывод:</b><br />Несмотря на недостатки пока считаю <a href="http://code.google.com/p/google-glog/">glog</a> победителем в плане простоты и эффективности. Надо конечно посмотреть как он в продакшне под большой нагрузкой себя покажет, но учитывая его простоту не думаю что будут проблемы.<br /><br />Достоен внимания и <a href="http://www.pantheios.org/">Pantheios</a> особенно если вам импонирует его архитектура.</div>Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com5tag:blogger.com,1999:blog-30809594.post-68919863957849545862011-09-05T14:46:00.000+04:002011-09-05T14:46:22.735+04:00Ubuntu One vs Dropbox на ubuntu serverЕсть у убунты <a href="https://one.ubuntu.com/">Ubuntu One</a> - очередная sync/share тулза на облаке. Имея несколько безголовых(для тех кто не в курсе - сервер без монитора,клавы и мыши) убунту серверов и необходимость синхронизировать некоторые некритичные несекретные(достаточно несекретные чтобы использовать сторонний сервис для синхронизации) данные не только между ними но и наружу(в том числе на не *nix системы) решил я попробовать Ubuntu One. Но оказалось что из консоли это можно сделать только с помощью жутких хаков (<a href="http://per.liedman.net/2011/01/22/using-ubuntu-one-for-backup-on-a-headless-server/">например</a>, да и то там в комментах пишут что уже не работает). Вобщем удивительное рядом - продукт компании не работающий на другом продукте компании.<br /><br />В итоге пришлось использовать dropbox. Вот тут например написано как это сделать: <a href="http://ubuntuservergui.com/ubuntu-server-guide/install-dropbox-ubuntu-server">http://ubuntuservergui.com/ubuntu-server-guide/install-dropbox-ubuntu-server</a>.<br /><br />P.S. Впрочем если погуглить то становится понятно что и на десктопе убунты большинство выбирают dropbox.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com2tag:blogger.com,1999:blog-30809594.post-38508572816267457462011-08-24T07:13:00.000+04:002011-08-24T07:13:05.827+04:00Параллелим на многоядерный кластер. C/C++ или Java.Этот пост не даст ответа на чём лучше писать программы работающие на кластере многоядерных машин, но даст немного информации к размышлению.<br /><br />Любой кто начнет искать "как бы написать что-нибудь для кластера" наткнётся на аббревиатуру <a href="http://ru.wikipedia.org/wiki/Message_Passing_Interface">MPI</a>. И наверняка наткнется на аббревиатуру <a href="http://ru.wikipedia.org/wiki/OpenMP">OpenMP</a> когда речь зайдёт о программировании для многоядерных систем.<br /><br />Когда заходит речь о таких сложных системах желание снизить влияние программистких ошибок на систему(ну или снизить порог уровня годных для этой задачи программистов) вполне имеет право на жизнь. Понятно что если вы сражаетесь за каждый такт процессора на каждом ядре - то у вас нет альтернативы C/C++ библиотекам MPI/OpenMP и команде сильных программистов с крепкой нервной системой. Если же копейки процессорного времени вас не очень волнуют и сильной команды C/C++ спецов нет, то можете взглянуть на Java с её библиотеками-аналогами (<a href="http://mpj-express.org">MPJ Express</a> и какой-то субъективно сомнительный на вид <a href="http://www2.epcc.ed.ac.uk/computing/research_activities/jomp/index_1.html">JOMP</a>). Но будьте готовы что для совсем простых задач "копейки" выльются в двукратное отставание как например <a href="http://habrahabr.ru/blogs/java/105408/">тут</a>.<br /><br />Что лучше "в среднем по больнице" сложно сказать. Явовские либы определённо имеют некоторые баги со стабильностью на некоторых системах (можете погуглить - вроде на солярке точно были проблемы у mpj), но в целом есть примеры работающие в продакшне, так что ничего критичного нет. На плюсах же наверняка у малоопытных несильных программистов будут большие проблемы одной из которой будет нахождение собственно проблемного кода. Но это мое субъективное мнение.<br /><br />Да, если у вас нету кластера, но есть относительно мощный компьютер вы можете запустить пару виртуалок и сделать кластер из них. Например если соберетесь под виндой запустить пару(ну или больше если система и здравый смысл позволяют) линуксов, то вот это поможет вам настроить сеть между двумя виртуалками: <a href="http://open-source-experiments.blogspot.com/2008/04/case-study-making-internal-networking.html">много букоф на английском</a>. Лично у меня этот вариант с парой убунту серверов и небольшой MPI программой на C++ сработал на ура.<br /><br />P.S. На всякий случай: если вы пишете что-то распределённое это вовсе не значит что вам необходим MPI. Но если у вас получается велосипед который повторяет функционал MPI - это повод задуматься.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-6620962833418675862011-03-02T02:15:00.000+03:002011-03-02T02:15:38.746+03:00Всё меняетсяПомнится давным-давно, году так в 2000-2001 читал я предсказания Билла Гейтса(если память не изменяет то в компьютерре) о том как всё будет с компьютерами очень скоро. Говорил он о том что почти весь десктопный софт будет скоро(вроде бы он говорил о 10-15 годах) переориентирован на тонких клиентов. И ведь многие смеялись тогда...<br /><br />Так о чём я. Если бы мне в 2000 году сказали бы что когда мне понадобится быстренько визуализировать некоторые сложные данные для внутреннего редкого использования, то в 2011 я возьму js-библиотеку и быстренько напишу на java генератор html для отрисовки нужных графиков с использованием этой js библиотеки - я бы повертел пальцем у виска. <br /><br />С одной стороны я конечно давно уже далёк от десктопных приложений и такое решение мне показалось оптимальным в том плане, что на совсем уж незнакомые грабли наступать не придётся. Ну и js либа уж больно красиво рисует. Года 4 назад я бы наверное налабал скрипт на perl с графической либой какой-нибудь (правда вышло бы уродливо, но наверняка ещё быстрее). В 2000 году мне даже мысль об использовании js для отрисовки не пришла бы в голову.<br /><br />Библиотека для рисования графиков выбралась сама по себе - я вспомнил что недавно Сироткин искал такую, и тупо позаимствовал у него первую попавшуюся ссылку: <a href="http://highcharts.com/">http://highcharts.com/</a>. <br /><br />На всё про всё у меня ушло несколько часов (там порядочно данных надо было обработать хитро прежде чем рисовать, ну и всё guice'фицировать и минимум unit-тестов сделать на случай если вдруг придётся потом в этом что-то менять) и в итоге нарисовалось всё очень наглядно и так красиво что аж жалко стало что это для внутреннего использования.<br /><br />Что будет через 10 лет сложно представить. Наверняка появится ещё пара концептуально других опций для решения этой задачи.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com1tag:blogger.com,1999:blog-30809594.post-15002198707725404782010-07-20T07:57:00.005+04:002010-07-20T13:42:26.261+04:00Тестирование сервлетов и Task Queue в GAE (с guice ServletModule).Я всегда считал что многие весьма опрометчиво думают что покрытие тестами всего и вся - это лучшее решение. Нет оно конечно лучшее в некоторых случаях, спору нет. Но когда проект небольшой, пишется для себя или проект создают 2-3 человека в очень короткий срок, то упор на тесты не всегда будет правильным решением (я не говорю что они не нужны совсем - просто надо знать меру и правильно оценивать соотношение пользы к затратам на создание тестов).<br /><br />Но бывает так что с тестами действительно сильно упрощается и ускоряется процесс разработки даже небольших проектов. Например как раз сейчас делаю проектик на <a href="http://code.google.com/appengine/">GAE</a> (Google App Engine) который достаточно сложный. Много cron-задач создающих task'и (здесь и далее имеются в виду task'и из Task Queue), которые в свою очередь сами могут порождать task'и. Так как cron и Task Queues на девелоперском сервере поддерживаются довольно-таки условно - возникли неудобства с проверкой работоспособности.<br /><br />Учитывая что в GAE есть всё для удобного локального тестирования (<a href="http://code.google.com/appengine/docs/java/tools/localunittesting.html">здесь</a>) было решено позапускать нужные cron-задачи и task'и в нужной последовательности в тесте и там же проверить результат. Очевидно это быстрее чем руками в девелопмент сервере запускать cron-задачи и руками же проверять результат. Более того - так как это тест, то нет нужды потом зачищать datastore чтобы попробовать запустить то же самое, но с другими параметрами. Вобщем экономия времени была ощутимой, не говоря уже о том что куда приятнее откинуться на спинку кресла и смотреть за прогрессом, чем руками рефрешить сервлеты.<br /><br />Запустить сервлет в тесте не так уж и сложно - я использовал <a href="http://httpunit.sourceforge.net/">httpunit</a>. Те кто используют обычные сервлеты могут использовать его. У меня же возникла проблема, все мои cron/task-сервлеты были <a href="http://code.google.com/p/google-guice/">guice'фицированы</a> и поэтому подход с httpunit не годился.<br /><br />Немного подумав я решил что достаточно будет написать mock's для HttpServletRequest и HttpServletResponse и использовать полученный с помощью guice объект сервлета чтобы всё это заработало. В итоге всё что получилось я выложил сюда: <a href="http://code.google.com/p/gaelocaltest/">http://code.google.com/p/gaelocaltest/</a>. Там же есть пример использования на главной странице.<br /><br />Конечно оно сделано весьма быстро на коленке и имеет ряд ограничений, но работает и вполне покрывает типовые задачи. Собственно кода там мало и он примитивен - так что желающие могут взять его за базу для своих целей если вдруг возможностей этого не хватит. Для тех кто не сталкивался с граблями по конфигурации для Local unit testing'а GAE, советую взглянуть на пример <a href="http://code.google.com/p/gaelocaltest/wiki/LocalServiceTestHelperConfig">конфига</a>. <br /><br />Вобщем это решение сэкономило мне просто уйму времени. Может быть кому пригодится.<br /><br />P.S. Для проектов имеющих посещаемость 1.5 человека в сутки и не имеющих постоянно "молотящих" cron/task сервлетов использование guice приведёт к частому появлению паузы при открытии сайта, так как из-за guice увеличится время старта. Сейчас правда все вроде лучше чем год назад - но народ периодически жалуется на эту багофичу GAE. Так что для таких проектов наверное не следует бежать внедрять guice - проще модифицировать мой код под httpunit (самому мне лень).Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com3tag:blogger.com,1999:blog-30809594.post-49638698199957590172010-05-17T19:13:00.004+04:002010-05-17T19:24:20.310+04:00Удалить индекс в GAE для Java проектаДля тех кто мучается с этой задачей, вот решение из гуглогруп:<br /><br />1) скачайте GAE python sdk<br />2) в вашем java проекте создайте файл app.yaml с таким содержимым:<br /><br />application: YOURJAVA_APPID<br />version: 1<br />runtime: python<br />api_version: 1<br /><br />handlers:<br />- url: .*<br /> script: main.py<br /><br />3) из директории вашего java проекте запустите (предполагается appcfg.py прописан в PATH)<br /> appcfg.py vacuum_indexes .<br /><br />4) удалите индексы которые хотите удалить<br /><br />5) удалите файл app.yaml <br /><br />6) зазвездите здесь чтобы не приходилось так извращаться: <a href="http://code.google.com/p/googleappengine/issues/detail?id=1893">http://code.google.com/p/googleappengine/issues/detail?id=1893</a><br /><br />P.S. А вообще это какой-то позор и дискриминация.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com5tag:blogger.com,1999:blog-30809594.post-65453979100933352822010-04-29T09:13:00.003+04:002010-04-29T10:36:01.256+04:00Быстрый способ обработать большой набор данныхДовольно стандартная минизадача - взять данные из одного или нескольких источников, сгруппировать и как-либо обработать их и выдать на выход в неком формате. Часто для группировки/обработки сильно помогает наличие SQL базы данных. Для минизадач напрашивается sqlite (для явы я использую <a href="http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC">http://www.xerial.org/trac/Xerial/wiki/SQLiteJDBC</a> но и вариант zentus вроде бы тоже использовал раньше). При больших объёмах данных узким местом обычно становится время затрачиваемое на insert/update. И тут сильно помогает то что в sqlite есть memory режим (имя базы данных для задания в конекте :memory:), что сильно ускоряет insert/update операции. Вобщем разница видна на глаз уже где-то на наборе из 500К объектов, а при отладке экономия времени просто неимоверная.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com2tag:blogger.com,1999:blog-30809594.post-85155227001959273402010-04-01T05:08:00.004+04:002010-04-01T05:33:33.262+04:00Про DTO в GAE+GWTНе знаю как остальных, а меня всегда раздражала необходимость создавать DTO (<a href="http://en.wikipedia.org/wiki/Data_transfer_object">Data transfer object</a>) в связке GWT-GAE. Ну не то чтобы раздражала, но не приводила в восторг необходимость клепать одно и то же каждый раз когда в клиентскую часть надо отдать данные объекта.<br /><br />Также всегда было ощущение что используя JDO, я уподобляюсь человеку забивающему гвозди микроскопом (вобщем понятно что какие-то экстра расходы на JDO уходят).<br /><br />Так что если у кого-то были схожие ощущения делюсь ссылкой: <a href="http://code.google.com/p/objectify-appengine/">objectify-appengine</a>. Из плюсов - не нужны DTO(конечно при использовании GWT-safe библиотек), быстрый холодный старт и тп. Сам пока в бою не щупал - пока только в планах. Даже есть проектик подходящий.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-72722877616965247192010-02-21T07:39:00.003+03:002010-02-21T07:46:20.042+03:00Java сообществаПодписался на некоторые java рассылки/сообщества. Поражен количеством людей не понимающих как оно работает и способных рассуждать только на уровне оберток/фреймворков. Интересно есть термин для программиста способного механически писать только в любимом фрейморке и не понимающего сути?Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com3tag:blogger.com,1999:blog-30809594.post-20835998463306468152010-02-14T07:00:00.004+03:002010-02-14T07:37:36.605+03:00Про будущее вёрсткиНе прошло и года, как подвернулся случай изучить "GWT 2.0 Layout system". Случай подвернулся после того как в очередном проектике IE успешно не смог ничего показать, хотя ничего сложного от него не требовалось. В общем впечатления от "standards mode" пока сугубо положительные. Я как человек верстающий крайне редко, всегда убивал много времени на несложные вещи типа хедер с двумя колонками, правая резиновая и тп. Теперь куча проблем решена за меня практически из коробки: <a href="http://code.google.com/webtoolkit/doc/latest/DevGuideUiPanels.html">http://code.google.com/webtoolkit/doc/latest/DevGuideUiPanels.html</a> . Задействование мозга конечно никто не отменял, но в целом многое упростилось и про хрень типа "а под IE мы напишем хак" в светлом будущем видимо можно будет забыть (да и сейчас при переходе на gwt layout'ы мой проектик мгновенно заработал в IE). Вобщем такими темпами скоро потребность в волшебных верстальщиках для проектов на GWT отпадёт.<br /><br />Кстати если кто не был на GDD09(как например я), то тут есть записи: <a href="http://code.google.com/intl/ru/events/developerday/2009/sessions.html">http://code.google.com/intl/ru/events/developerday/2009/sessions.html</a>. Пробежавшись по заголовкам я просмотрел <a href="http://www.youtube.com/watch?v=K-ffOcxMb0U">"GWT App Architecture Best Practices"</a>. В принципе сейчас это и в хелпе есть частично (про MVP), но всё равно советую посмотреть. На русском смотреть не советую - переводчик жжот местами. Жаль не было этой презентации года 3 назад, когда я городил похожее сам и собирал все шишки тоже сам (например злоупотреблял шиной для посыла всего и получал дикую прожорливость и тормознутость из-за этого - тут же докладчик сразу говорит что так делать не стоит, что сбережёт многим много нервных клеток). Правда на практике не всё так гладко как он рассказывает получается с MVP, но для ознакомления с концепцией в принципе и этого примера вполне достаточно чтобы загрузить средний мозг. А все сложности можно решить - главное понимать идею.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-89112454128549610802010-02-09T17:16:00.003+03:002010-02-09T17:29:07.841+03:00Лучший способ убить GAEЕсли вдруг у вас в логах появились сплошные сообщения "Request was aborted after waiting too long to attempt to service your request. Most likely, this indicates that you have reached your simultaneous dynamic request limit. This is almost always due to excessively high latency in your app. Please see http://code.google.com/appengine/docs/quotas.html for more details." и вы недавно задеплоили новую версию, то не стоит первым делом проклинать гугл который не даёт вам по крону запускать относительно лёгкий на ваш взгляд скрипт. Потому что заподозрив гугл в плохом я стал искать признаки того что моя jvm постоянно релоадится из-за того что на сайт нету нагрузки извне и заниматься прочими глупостями. А всё оказалось очень просто. Относительно несложный regex натравленный на пару килобайт текста сжирал весь процессор в одну харю ну а дальше по цепочке не хватало процессора всем бэкграундным скриптам (которые впрочем успевали переодически пробиваться и худо-бедно работать). Самое хреновое что при этом почему-то и логгинг не работал(те чекпойнты заботливо прописанные по скрипту тупо ничего не писали в лог, а писалось только сообщение которое я привел выше), так что выделив методом исключения фрагмент кода на котором GAE ложился пришлось тупо догадаться в чём дело. :)Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-39398445182651573002010-02-07T13:35:00.003+03:002010-02-07T13:57:38.832+03:00GAE как сделать rollback в javaПочему-то GAE сильно ориентирован на питонистов и в ошибках часто даёт подсказки только для них. Или может считают что те кто на джаве пишут сами догадаются...<br /><br />Вобщем наконец я смог сломать своё приложение до состояния когда пришлось пользоваться консолью. Во время очередного деплоймента мне показалось что оно зависло и я отменил его и перезапустил eclipse. В итоге стал получать сообщение вида: "Another transaction by user *** is already in progress for this app and major version. That user can undo the transaction with appcfg.py's "rollback" command.". Вобщем пришлось лезть к консольным GAE тулзам в eclipse (*path to eclipse*)/plugins/com.google.appengine.eclipse.sdkbundle_VERSION/appengine-java-sdk/bin/ и там запустить (вариант для винды) appcfg.cmd -e vasyapupkin@gmail.com rollback <путь к war директории проекта eclipse>.<br /><br />Также попробовал GAE "Task Queues". На мой взгляд замечательная вещь, очень удобно по сравнению со стандартными велосипедами на кроне. Жаль пока это экспериментальная фича и повально использовать даже в своём тестовом проектике я не буду пока.<br /><br />Из новых обнаруженных небольших недостатков GAE:<br />- сторадж не очень стабилен, т.е. ситуации когда что-то не удаляется или не добавляется не так уж редки, что для серьёзных задач наверное сильно добавит гемороя;<br />- в java невозможно удалить неиспользуемые индексы (в гуглогруппах советуют сделать это из питоновского sdk), это ни на что не влияет но раздражает :).Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-52942144177637800882009-09-28T06:02:00.006+04:002009-12-13T21:10:49.925+03:00GAEЧуствую GAE меня добъёт. Таких чудес насмотрелся - что на всю жизнь хватит. Надо будет поплотнее сесть, чтобы разобраться. Из последнего: в базе один основной объект с кучкой owned и unowned объектов (разных классов). Так вот через query без фильтров он возвращается как надо, а через getObjectById возвращается без child'ов. Как такое получается пока понять не смог. Из полезного - обнаружил что есть усечённая админ панель для локальной разработки, что облегчает жизнь ощутимо: <a href="http://localhost:8080/_ah/admin">http://localhost:8080/_ah/admin</a> (в последних билдах <a href="http://localhost:8888/_ah/admin">http://localhost:8888/_ah/admin</a> ) .<br /><br />Update: разгадка-решение к чудесам - (defaultFetchGroup="true") . Расписывать не буду - гуглиться в соответствующих гуглогруппах и не только там.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-29236671396751784502009-08-03T14:57:00.003+04:002009-08-03T16:42:57.455+04:00Тестирование в GWTПрочитал занимательную статью про тестирование GWT приложений: <a href="http://code.google.com/webtoolkit/articles/testing_methodologies_using_gwt.html">http://code.google.com/webtoolkit/articles/testing_methodologies_using_gwt.html</a>. Интересный подход с MVP и EasyMock, но на мой взгляд слишком затратный в плане оверхеда. Т.е. для небольших проектов вряд ли стоит такое воротить. Также полезная ссылка о том как тестировать RPC в GWT: <a href="http://code.google.com/docreader/#p=google-web-toolkit-doc-1-5&s=google-web-toolkit-doc-1-5&t=DevGuideJUnitAsync">http://code.google.com/docreader/#p=google-web-toolkit-doc-1-5&s=google-web-toolkit-doc-1-5&t=DevGuideJUnitAsync</a>.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-59010176513080969952009-07-19T13:45:00.003+04:002009-07-19T14:38:30.470+04:00Про GAE и GWTНедавно я решил плотно пощупать Google App Engine (GAE), а также подтянуть скилы по GWT. Надо сказать что с появлением Google плагина для Eclipse жизнь заметно упростилась и пропали например баги с RPC которые были относительно недавно и даже побудили меня переносить проект под нетбинс. Вобщем для того чтобы собрать и выкатить проект на GWT+GAE (на яве) никаких шаманств мне пока совершать не пришлось. Никаких глюков пока тоже не заметил.<br /><br />Когда начал писать проект в первые пару дней показалось вот оно счастье - настолько всё удобно и продуманно. Удобно отлаживать когда серверный и клиентский код доступен в одном дебагере и написан на одном языке, удобно выкатывать приложение(нажал кнопку и оно уже там). Но потом нашлись некоторые минусы. Например интернационализация. Я выбрал наиболее правильный на мой взгляд метод - с помощью статических строк(static string internationalization). Но на серверной стороне его использовать невозможно(потому что нужно использовать GWT.create) и возникает проблема когда например надо кинуть исключение на серверной стороне RPC с локализованным текстом. Пока ничего умнее как создание специального класса исключения с enum'ом всех типов исключений, и класса который на клиентской стороне производит получение нужного текста по типу исключения с учётом текущей локали я не придумал. Вобщем пустячок а неприятно - усложняет слегка жизнь. Второе разочарование постигло когда выяснилось что в GAE Datastore не предусмотрено создание unique ограничений. Но в целом пока впечатление сильно положительное, буду смотреть что там дальше будет при более глубоком погружении.<br /><br />P.S. Да чуть не забыл - отсутствие какого-либо GUI редактора/просмотрщика данных для datastore очень огорчает.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-1903072546629590092009-01-28T05:46:00.003+03:002009-01-28T06:00:19.719+03:00Темы GWT. фичобаг<div style="text-align: justify;">Если вы делаете достаточно сложный с точки зрения вёрстки проект, и что-то у вас результат не всегда соответствует тому что вы хотите, и сильно отличается в браузерах, то советую вам взглянуть на магическую незакомментированную строчку в вашем XXX.gwt.xml файле: <inherits name="'com.google.gwt.user.theme.standard.Standard'/"><br /></div><br /><div style="text-align: justify;">Наверное они хотели как лучше, а получилось что получилось. Я конечно понимаю что новичкам возможность выбрать одну из предустановленных тем понравится наверное, но зачем делать стандартную тему которая мешает втыкать gwt кусок в большую сложную страницу со своим css - это для меня загадка. Вобщем закомментировав указанную выше строчку вы обретёте счастье.</div>Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com3tag:blogger.com,1999:blog-30809594.post-53426312077682938062008-09-29T13:20:00.003+04:002008-09-29T14:46:22.494+04:00Настройка дополнительных клавиш под Linux<div style="text-align: justify;">Решил задействовать дополнительные клавиши моей относительно недавнокупленной Logitech Wave Keyboard. С удивлением увидел что утилиты под иксы которая бы подхватила мою клаву прямо "из коробки" нету(напомню - у меня fedora), более того настроить дополнительные клавиши тоже нечем - они игнорятся теми настройщиками какие есть. Править конфиги руками - лениво мне, всё же не 90-е уже. И тут я нашёл то что нужно в репозитории от Tigro (если кто не знает: <a href="http://tigro.info/">http://tigro.info</a> ) - keytouch и keytouch-editor.<br /></div><br /><div style="text-align: justify;">keytouch правда тоже мою клаву не знал, но keytouch-editor осилил мою клаву, правда комбинации типа Fn-F7 приходится нажимать хитро сначала жмём Fn, потом мышкой кнопку New а потом уже F7. Иначе в сканкод у вас запишется код клавиши Fn. После работы в keytouch-editor результаты сохранил в файл который потом подгрузил в keytouch. Жить с настроенными дополнительными клавишами стало веселее и более эффективно.<br /></div>Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-74431257974741717222008-09-16T04:14:00.002+04:002008-09-16T04:37:56.450+04:00Работа с удалённой ФС через ssh<div style="text-align: justify;">Довольно типичная задача когда необходимо быстренько что-то залить на сервер или перекинуть с сервера на сервер, причём ftp на серверах нет и не будет(по разным причинам). Когда это "что-то" - мои исходники/скрипты и т.п., то я использую svn потому что такие вещи у меня всегда в репозитории. Но что делать когда это например гигантский дамп БД, куча логов и тп? Тогда я обычно использовал scp - отлично копирует, но по юзабилити конечно консольно минимилистична. В принципе с помощью scp можно сделать всё что нужно. Но не очень удобно в некоторых случаях.<br /></div><br /><div style="text-align: justify;">А удобнее использовать fuse и sshfs (в федоре пакеты fuse и fuse-sshfs). Тогда для того чтобы подмонтировать удалённую ФС необходимо сделать что-то типа такого:<br /></div><span style="font-family: courier new;">sshfs root@server.com:/path/on/server /path/on/local</span><br /><br /><div style="text-align: justify;">Тогда в /path/on/local у вас будет доступна /path/on/server находящаяся на сервере server.com. Отмонтировать можно например так:<br /></div><span style="font-family: courier new;">fusermount -u /path/on/local</span><br /><br />Вобщем в некоторых случаях sshfs экономит кучу времени.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-10237348271143284612008-08-23T00:59:00.004+04:002008-08-23T02:31:50.702+04:00Организация бэкапов в linux<div style="text-align: justify;">Долгое время я вполне обходился рукописным велосипедом для создания бэкапов. Ну т.е. использовал tar с небольшими скриптами.<br /></div><br /><div style="text-align: justify;">Но недавно наткнулся на достаточно удобную утилиту написанную на perl - <a href="http://www.rsnapshot.org/">rsnapshot</a>. Она использует rsync и hard links для создания серии бэкапов с заданными промежутками (у меня например создаются каждые 4 часа, каждый день и каждую неделю). При этом можно не просто указать что бекапить но и задать скрипты запускающиеся до/после очередного бекапа. Также есть много полезных функций например есть diff (например rsnapshot diff daily.0 daily.1 сравнит последний и предпоследний ежедневные бекапы), rsnapshot du покажет сколько отожрано места под ваши бекапы (отжирает надо сказать не так уж и много - читайте подробнее по ссылкам в заметке). Вобщем изучение man'а занимает минуты, всё просто и удобно - советую.<br /><br />Перечислю вкратце плюсы которые я получил. Даже без сжатия бэкапы занимают мало места, делаются быстро. Так как бэкапы не сжаты то мгновенно можно посмотреть всё что нужно (например меняли конфигурацию утилитой и что-то поломалось - хотите посмотреть что поменялось по сравнению скажем со вчерашним конфигом), а не ждать пока распакуется многогиговый зазипованный tar файл. Можно не страдать паранойей и не коммитить в svn каждый чих - хотя конечно это спорно и не для всех подходит. Но для проектов которые вы ведёте с одного компьютера вполне применимо.<br /><br />Ссылки по теме:<br /><ol><li><a href="http://www.mikerubel.org/computers/rsync_snapshots/">http://www.mikerubel.org/computers/rsync_snapshots/</a> - желающим соорудить свой велосипед во чтобы то ни стало :)</li><li><a href="http://gentoo-wiki.com/HOWTO_Backup_to_DVD">http://gentoo-wiki.com/HOWTO_Backup_to_DVD</a> - о тонкостях бэкапа на DVD</li><li><a href="http://www.linux-backup.net/App/">http://www.linux-backup.net/App/</a> - куча ссылок по теме, но много устаревших<br /></li></ol></div><br />P.S. да и делайте бэкап не на тот же диск на котором данные которые вы бэкапите. Помните закон Мерфи - бэкап нужен тогда когда его нет.Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0tag:blogger.com,1999:blog-30809594.post-38165164390370677462008-05-18T18:10:00.008+04:002008-06-28T03:16:03.028+04:00Сходил на YAPC::Russia 2008Сходил на <a href="http://2008.perlrussia.ru/">May Perl</a>, впечатления смешанные.<br /><br /><span style="font-weight: bold;">Для начала впечатления от докладов, которые я посетил.</span><br /><br />1 день:<br /><div style="text-align: justify;">IO::Lambda - в принципе для меня это было что-то что я не трогал руками, но имею в планах попробовать - было интересно. Конечно то что модулю всего полгода несколько не радует, но с другой стороны свежие модули оперативней патчат по моим наблюдением, так что посмотрим.<br /></div><br /><div style="text-align: justify;">Профилирование perl-кода - в принципе несмотря на то что большая часть информации для меня не была новой, но например kcachegrind надо будет попробовать (cправедливости ради отмечу что слышал и раньше про неё, но по-моему не использовал).<br /></div><br /><div style="text-align: justify;">May POE - ну доклад в принципе грамотный, но мне не особо интересный, так как новой информации там для меня было (да и в целом доклад в основном обзорный был).<br /></div><br /><div style="text-align: justify;">Потом я пошёл на мастеркласс POE - но там были проблемы с инетом, поэтому всё пошло как-то сумбурно. В целом наверное зря я на него пошёл - мастеркласс мне был не нужен, я собственно хотел задать один вопрос (задал только во второй день поймав Серёжкина с утра, но об этом ниже). Вобщем поскучал я тут, в отличие от тех кого поражала/развлекала скорость эффективного кнопкодавления в *nix консоли и emacs'е - я 90% времени живу в никсах, поэтому меня это не развлекло. Вобщем со стороны конечно виднее - но можно было и без инета придумать чего-нибудь (как вариант запустить apache/nginx и его дрючить POE или носить с собой usb-bluetooth и мобилу на случай отсутствия инета(хотя такой тонкий канал поможет не всегда)).<br /></div><br /><div style="text-align: justify;">"Аргументы в пользу XSLT" - на мой взгляд инородный доклад. Лично мне абсолютно неинтересен был - аргументы и за и против XSLT давно известны всем интересующимся, а про какое-то приложение к perl'у не было вообще ничего сказано.<br /></div><br /><div style="text-align: justify;">Построение совместимых Perl/mod_perl систем с веб-интерфейсом - чего-то я вообще не помню этого доклада. Либо его отменили, либо не зацепило совсем(судя по названию это очень может быть).</div><br /><br />Perl и Web 2.0 - несмотря на то что ничего особо нового для меня не было сказано, было весело и запомнилось. Не зря докладчику дали приз. :)<br /><br />2-й день:<br /><div style="text-align: justify;">Блицы вобщем понравились все. Единственное что про обфускатор Stunnix мне было неинтересно(всё очевидно и уж больно самопиарно, хотя может кому полезно будет), ну а "CGI.pm - зло?!" не очень вписывается в формат блиц-доклада на мой взгляд, т.к. вопросы из зала наверняка бы были. :) Ну и у Серёжкина блиц получился не до конца</div><br /><br />Perl+виртуализация - меня не зацепило, но возможно я просто не проникся до конца.<br /><br /><div style="text-align: justify;">UTF Perl Practice - в принципе полезный доклад для тех кто не хочет сам пройтись по граблям. Жаль что его не было лет 5 назад когда я на эти грабли наступал изо всех сил. :)</div><br /><br /><div style="text-align: justify;">CMS для крупного портала на примере TRAVEL.RU - ну реально работающие решения всегда интересны. Докладчик грамотно отбивался от каверзных и не очень вопросов - видно что не в первый раз. :)<br /></div><br /><div style="text-align: justify;">ООП. Parrot. - Ну тут для меня тема была относительно новая, да и у Серёжкина на этот раз были заранее приготовленные слайды так что доклад был интересен и безэксесен :).<br /></div><br /><div style="text-align: justify;">YAPC::Russia::Golf - вобщем задачи были не из разряда моих любимых, да и в субботу у меня было много дел после которых в воскресенье болела голова, поэтому я забил на них. :) Но решение-победитель про делимость на 11 удивило, так как в перерыве начали прикидывать как можно сделать, но ничего похожего даже близко не придумывалось.<br /></div><br /><span style="font-weight: bold;">Теперь не про доклады, а про разное:</span><br /><div style="text-align: justify;"><br />Во время обеда оказался за одним столом с другими перловиками и выяснил что POE полетел не так как надо не только у меня. Т.е. не у меня одного он тёк по копейке в минуту, и не меня одного его скорость не впечатлила. Вобщем после этого я передумал по этому поводу пытать Серёжкина - может действительно для его задач и скорость достаточная, и утечки не критичны (там вобщем действительно текло помаленьку, ну и как кто-то сказал ничто не мешает убить и перезапустить скажем раз в сутки).<br /><br />Но на второй день случайно поймав Серёжкина, всё же спросил его про POE. Он сказал что у него оно работает и не течёт, но версия 1.0002. Я же пробовал давно и наверняка более младшую версию. Вобщем придётся видимо опять уколоться и пожрать кактус.<br /></div><br /><br /><span style="font-weight: bold;">Про организацию:</span><br />Футболки выдали, кофебрейки были с едой и питьём - вроде всем хватило. Вобщем в этой части всё нормально.<br /><br /><div style="text-align: justify;">Из минусов - накладки в докладах одного из организаторов (это я про Серёжкина), это как-то не очень радовало. Хотя я иногда малотерпим к чужой неорганизованности, поэтому может излишне предвзят. Ну и кроме того к чему-то придраться надо, а тут вроде как и по делу... :)<br /><br /><span style="font-weight: bold;">Добавление:</span><br />Забыл про два доклада вошедшие вне программы:<br />ПеАр LinuxFormat - на удивление прошёл на удивление легко.<br />ORM - местами было интересно, хотя я пока для своих задач не вижу смысла в нём особого.<br /><br /><span style="font-weight: bold;">Добавление 2:</span><br />Забыл написать что в целом неплохо, но могло быть лучше. :)<br /><br /><span style="font-weight: bold;">Update 3: </span>решил дать ссылку на страницу где есть ссылки на презентации а не втыкать их сюда по одной :) : <a href="http://event.perlrussia.ru/yr2008/talks">http://event.perlrussia.ru/yr2008/talks</a><br /><br />В рассылке jerom выложил новое мегарешение гольфа про деление на 11 (автор некто mtve):<br /><pre wrap="">#!perl -lp<br />$_=s/..?\b/"@:[0..$&]"/e?redo:/^( {11})*$/?yes:'no'</pre>Вобщем я поражён, пока больше и сказать нечего. :)<br /><br /><span style="font-weight: bold;">Update 4:</span> видео с конференции здесь: <a href="http://event.perlrussia.ru/yr2008/media/video.html">http://event.perlrussia.ru/yr2008/media/video.html</a><br /></div>Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com6tag:blogger.com,1999:blog-30809594.post-28693980214065746072008-05-07T00:20:00.000+04:002008-05-07T00:20:00.865+04:00vim как удобный diff (часть 2)<div style="text-align: justify;">В предыдущем посте <a href="http://pseudo-tech-notes.blogspot.com/2008/05/vim-diff.html">vim как удобный diff</a> я уже описал как использовать vim в качестве diff. Но погуглив нашёл что оказывается есть дополнительная настройка которая делает использование vim для этой задачи ещё удобнее. Настройка называется diffopt и по умолчанию установлена в filler:<blockquote>:set diffopt=filler</blockquote>Это заставляет оба столбца скролится синхронно. Но кроме этого можно задать игнорирование начальных и конечных пробелов, и кол-во пробелов подряд:<br /><blockquote>:set diffopt+=iwhite</blockquote>Ну и конечно можно задать игнорирование регистра при сравнении:<br /><blockquote>:set diffopt+=icase</blockquote>Также можно задать кол-во строк между "схлопнутыми одинаковыми фрагментами" и отличающимися фрагментами (коряво описал - но прямее не получается чего-то), по умолчанию выставлено в 6:<br /><blockquote>:set diffopt+=context:2</blockquote>Пример того что получается при выставленном icase и context:2 приведён ниже(я добавил в f1 и f2 дополнительных 8 строк чтобы fold'инг был виден):<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp0.blogger.com/_cR10U53OBxA/SB-zxnVtKOI/AAAAAAAAAac/jhAMvr0SEkE/s1600-h/vimdiff2.png"><img src="http://bp0.blogger.com/_cR10U53OBxA/SB-zxnVtKOI/AAAAAAAAAac/jhAMvr0SEkE/s400/vimdiff2.png" style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp0.blogger.com/_cR10U53OBxA/SB-zxnVtKOI/AAAAAAAAAac/jhAMvr0SEkE/s400/vimdiff2.png" alt="" id="BLOGGER_PHOTO_ID_5197070159963826402" border="0" /></a><br /></div>Dmitriy T.http://www.blogger.com/profile/12257889673961252182noreply@blogger.com0