Посты с тэгом «debug»

  • XSL Tracer

    Я, наконец-то, более-менее довёл до ума свой новый проект, которым хочу поделиться с народом.

    Проект называется XSL Tracer. Его задача заключается в том, чтобы ответить на простой вопрос: откуда после XSL-трансформации пришёл тот или иной элемент? Как это работает можно увидеть на демо-странице. Там отображается результат XSL-преобразования — обычный HTML-документ. Кликнув по любому элементу можно получить подробную информацию о нём:

    • XSL: шаблон, где выводится элемент.
    • Context: контекстный XML (на который сработал матч, в случае xsl:apply-templates). Сам элемент выводится урезанным (только 10 внутренних тэгов), чтобы в случае, если это будет корневой элемент, браузер не умер в муках (да и толку от полной структуры никакого).
    • Source: место в шаблоне, где непосредственно генерируется выбранный элемент. Очень удобно в случае, если делаете сopy-of результатирующей структуры (в примере это html-код, заданный внтутри переменной).
    • Call stack: стэк вызовов apply-template/call-template/apply-imports, через который дошли до вызова текущего шаблона
    • Inner calls: список внутренних вызовов шаблонов, которые по каким-то причинам нельзя отобразить (по крайней мере пока) в документе. Например, вызов шаблонов, выдающих атрибуты для выбранного элемента, или его текстовое содержимое. Список отображается под Call stack, и только в том случае, если такие вызовы есть.

    Демка нормально работает в Safari/Firefox/Chrome, Opera жутко тормозит на больших документах, а в IE даже не открывал.

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

    Как это работает

    Проект состоит из двух частей: бэкэнд и фронтэнд.

    На бэкэнде используется Saxon 6.5 (пока эта версия, так как в 9.2 лично у меня вылезло куча проблем с EXSLT). У него есть встроенный механизм трассировки, который по умолчанию генерирует результат в виде XML (такой файл на реальных проектах вырастает до нескольких мегабайт). Я написал свой трассировочный класс, который выводит данные в более компактном и лёгком JSON-формате, а также дополнительные данные вроде списка используемых в трансформации XSL и XML (подключённых через document()) документов. А также он делает дополнительные штуки вроде резолвинга result-tree фрагментов при вызове xsl:copy-of.

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

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

    Как запускать

    1. Должна быть установлена Java (версия особо не важна).
    2. Скачиваем Saxon 6.5
    3. Скачиваем трэйсер
    4. Генерируем трассировку вот такой командой в консоли:

    java -classpath /path/to/tracer.jar:/path/to/saxon.jar ru.imobilco.XSLTracer -to /path/to/trace-result.html /path/to/input.xml /path/to/template.xsl

    В случае успешной работы трэйсера сгенерируется файл trace-result.html, который можно сразу открывать в браузере (CSS и JS берутся с моего сервера). Если возникла ошибка во время трансформа, то файл будет сгенерирован, но вместо документа увидите нормальное сообщение об ошибке. В случае ошибки работы Java, увидите сообщение в консоли.

    Надо помнить, что Saxon очень чувствителен ко всяким всяким отхождениям от спецификации, поэтому то, что работало в Xalan/libxsl/где-то ещё может не сработать в Saxon. Но исправления, как правило, довольно мелкие и незначительные.

    Онлайн-версия

    Мой коллега Лёха Баранов написал сервлет, который позволяет запускать через веб, передавая ссылки на XML и XSL файлы. Для него был заведён специальный проект xmltools.ru. Работает просто: указываете ссылки на файлы и, если нужно, данные для http-авторизации. Далее сам трансформ находит все зависимые файлы, скачивает их и делает трансформ.

    Но с онлайн-версией не так всё просто. В частности, когда результат отдаётся фронтэнду, он тоже начинает скачивать внешние файлы через аякс. И, как вы уже, наверно, догадались, включается ограничение Same-Origin Policy, которое не позволяет загружать данные с другого домена. Вариантов решения два: либо вы свой сервер настраиваете на cross-domain ajax, либо используете xmltools.ru как сервис, запрашивая трансформ через какой-нибудь php/python/ruby скрипт, отдавая результат трассировки со своего домена.

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

    Метки: , , ,
  • Краткий экскурс в профайлинг JS

    Недавно с коллегой переписывался о том, как нужно делать профайлиг JavaScript-кода. Решил, может информация будет еще кому-нибудь полезна. Предыстория: коллега делает анимацию на JS, пытаемся понять, почему она сильно тормозит.

    Внёс изменения. Поставил таймер на mouseOver. Получается, что функция calculateSize выполняется 20-23ms. До этого примерно так и было.

    Нужно не таймером, а профайлером замерять (есть в Firebug и последних версиях Webkit).

    Ну я имею в виду функцию FireBug’а console.time() и console.timeEnd(). Они делают то же, что профайлер, только для определенного куска кода.

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

    Я просто не могу понять в этом профайлере где именно тормозит. Там всё исписано в init() 🙂

    Вот смотри, сейчас профайлер тебе говорит, что во время работы твоего эффекта больше всего ресурсов жрет функция init() из jQuery, которая вызывается почти 6000 раз:

    firebug1

    Так как сама по себе эта информация нам мало полезна, тебе нужно найти ту функцию, которая так часто вызывает init(). Для этого отсортируем данные по колонке Time (общее время выполнения функции). Видим, что на первом и втором месте стоит jQuery-функция, а на 3 и 4 — функции calculateSize() и imgSize(), время выполнения которых — 588 и 547 мс соответcтвенно.

    firebug2

    Тут явно видно, что сами эти функции выполняются очень быстро (см. Own Time), но вот они вызывают другие функции, которые и дают те самые 588 и 547 мс.

    Смотрим calculateSize(), проблемные места:

    var prevImg = slctdImg.parent().prevAll();
    var nextImg = slctdImg.parent().nextAll();
    

    Это все выборки по дереву, очень тяжелые операции.

    Смотрим imgSize(), проблемные места:

    imgCont.width(newWidth).height(newHeight);
    imgCont.css({
    	width: newWidth,
    	height: newHeight,
    	margin: (!dir) ? "0 0 " + k + "px 0" : k + "px 0 0 0",
    	zIndex: count - num,
    	opacity: 1 - (num) / 30
    });
    

    два раза устанавливаешь width и height

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

    Метки: , , ,