Archiwum miesiąca: wrzesień 2010

Profilowanie kompilowanych binarek w Gentoo

Gcc, najczęściej używany kompilator w Gentoo (czy ktoś zna inne kompilatory, którymi da się skompilować system dający uruchomić się?) posiada możliwość profilowania kompilowanego kodu. Wykorzystując wcześniej przygotowane dane, stara się on utworzyć kod wykonywalny, który teoretycznie[1] powinien działać szybciej niż kod nieprofilowany.

Odpowiednie flagi gcc to -fprofile-generate oraz -fprofile-use. Program skompilowany z flagą -fprofile-generate będzie tworzył pliki .gcda w katalogu roboczym. Takie tworzenie plików „gdziekolwiek” nie jest zwykle pożądane, lepiej aby te pliki były generowane w określonej lokalizacji. Pomocą służy nam opcja -fprofile-dir= z której pomocą możemy wskazać katalog w którym będą tworzone pliki *.gcda . Musimy jednak wiedzieć, że ta flaga pojawiła się w GCC dopiero od wersji 4.4.0 .
Wiemy już w jaki sposób możemy możemy wybrać miejsce w którym będą tworzone pliki potrzebne do profilowania. Krok do przodu i kolejny problem, czyli możliwość wystąpienia kolizji. Częstą sytuacją jest, że pliki źródłowe mają taką samą nazwę w różnych pakietach. Dobrym rozwiązaniem wydaje się być utworzenie podkatalogów o nazwie tworzonej na podstawie nazwy pakietu. I co? Może mamy dla każdego pakietu zmieniać flagę -fprofile-dir=/sciezka/do/gcda/nazwa_pakietu ? Jest to trochę mało wygodne.
Ciekawe rozwiązanie znalazłem na rosyjskojęzycznym forum dotyczącym Gentoo, użytkownicy stworzyli modyfikacje do helpera o nazwie emake. Cała „obsługa” sprowadza się do ewentualnego ustawienia katalogu bazowego dla plików .gcda , a wybór czy tego czy binarka ma być skompilowana tradycyjnie, z opcją aby tworzyła pliki profilowe lub skompilowana z użyciem tychże plików dokonywany jest poprzez zmienną FEATURES.
Podczas testowania tego rozwiązania natrafiłem na problem, make dosyć często ignorował CFLAGS ustawione jako zmienne środowiskowe (kwestia budowy Makefile?), użycie make w następujący sposób: make CFLAGS=fprofile-use uznaję za zbyt inwazyjne, nadpisuje flagi użytkownika oraz wprowadza flagi, które mogły być usunięte przez ebuild (filter-flags). Uznałem, że lepiej będzie gdy dodatkowe flagi będą dodane do istniejących zanim będzie pakiet konfigurowany (zazwyczaj przed słynnym ./configure). Niektóre pakiety (np.bzip2) nie mają w ogóle fazy src_configure() dlatego umieściłem  ten kod w src_unpack().  Dzięki temu CFLAGI zostaną, z pomocą configure, prawidłowo umieszczone w Makefile. Najlepszym miejscem do modyfikacji portage wydaje się mi plik /usr/lib/portage/bin/ebuild.sh. Dokonałem kilku drobnych zmian do oryginalnego rozwiązania, plik różnicowy wygląda tak:

--- ebuild.sh.orig      2010-09-03 14:23:46.000000000 +0200
+++ ebuild.sh   2010-09-03 14:44:29.400038418 +0200
@@ -453,6 +453,33 @@
 # should be preserved.
 find . -mindepth 1 -maxdepth 1 ! -type l -print0 | \
 ${XARGS} -0 chmod -fR a+rX,u+w,g-w,o-w
+
+       # New implementation with profiling on compilation support
+       # source "${PORTAGE_BIN_PATH}/isolated-functions.sh"  &>/dev/null
+       PROFILE_DIR="${PROF_DIR:-/var/tmp/profile}"
+       PROFILE_SUBDIR="${PROFILE_DIR}/${CATEGORY}/${PF}"
+       GCC_VERSION=$(gcc --version | awk '{print $3;exit}')
+       if has profile ${FEATURES} ; then
+                       if [[ ${GCC_VERSION} < 4.4.0 ]]; then
+                               ewarn "\nGCC version is too old to use compilation with profile usage."
+                               ewarn "GCC 4.4.0 or higher required."
+                               ewarn "Falling back to compilation w/o profile usage.\n"
+                       elif has profile-use ${FEATURES} ; then
+                               if [[ -d ${PROFILE_SUBDIR} ]] ; then
+                                       einfo "\nCompiling with profiling data usage.\n"
+                                       CFLAGS+=" -fprofile-use -fprofile-dir=${PROFILE_SUBDIR} -Wcoverage-mismatch -fprofile-correction"
+                                       CXXFLAGS="${CFLAGS}"
+                               else
+                                       ewarn "\nNo directory with profiling data. Did you run fist stage and then program itself?\n"
+                               fi
+                       else
+                               einfo "\nCompiling with profiling data gathering support.\n"
+                               mkdir -m 777 -p "${PROFILE_SUBDIR}"
+                               CFLAGS+=" -fprofile-generate -fprofile-dir=${PROFILE_SUBDIR}"
+                               CXXFLAGS="${CFLAGS}"
+                               LDFLAGS+=" -fprofile-arcs"
+                       fi
+       fi
 }

 strip_duplicate_slashes() {

Alternatywą jest pobranie pliku-patcha z adresu: http://repoz.mejor.pl/svn/gentoo/distfiles/ebuild-2.1.8.3-profile.patch.
Lista nowych opcji jakie możemy ustawić w /etc/make.conf:
PROFILE_DIR=< ścieżka do katalogu, poniżej którego będę tworzone pliki profilujące>, domyślna wartość to /var/tmp/profile
W FEATURES można ustawić:
profile – kompilacja z ustawieniem flagi aby tworzyć pliki służące profilowaniu
profile-use – kompilacja z użyciem plików profilujących, aby ta flaga działała musi być również ustawiona opcja profile

To co? Wszystko jest omówione? A co jeśli program zostanie uruchomiony najpierw przez roota, a potem zwykły śmiertelnik będzie chciał także go uruchomić? Wtedy dostanie listę komunikatów z serii permission denied dla wszystkich plików .gcda , i tyle byłoby z profilowania.
Rozwiązałem to z użyciem ACL, czyli musimy pamiętać aby partycja, na której będę zbierane te pliki (nie są duże) była zamontowana z obsługą ACL.
# getfacl profile/
# file: profile/
# owner: root
# group: root
user::rwx
group::rwx
other::rwx
default:user::rwx
default:group::rwx
default:other::rwx

Tak nadajemy te uprawnienia:
setfacl -d -m g::rwx /var/tmp/profile
setfacl -d -m o::rwx /var/tmp/profile
setfacl -m g::rwx /var/tmp/profile
setfacl -m o::rwx /var/tmp/profile

[1] – ktoś pokusi się i sprawdzi na ile takie profilowanie przyśpiesza wykonywanie programu? I zaprezentuje zarówno pozytywne jak i negatywne wyniki testów pomiarów szybkości ;)

P.S. Właśnie zauważyłem, że ta funkcja nie działa przy live ebuildach.