Ultimo test su Ubuntu 20.04 con glibc versione di sviluppo 2.33.9000 (vedi glibc/version.h
) il 27 giugno 2021.
Come scaricare e creare glibc ed eseguirne i benchmark
Puoi ottenere manualmente il codice sorgente di glibc qui:https://www.gnu.org/software/libc/sources.html:
git clone https://sourceware.org/git/glibc.git
cd glibc
git checkout master
Mirror di terze parti su GitHub:https://github.com/bminor/glibc/tree/master/benchtests
Vedi anche:
- https://kazoo.ga/a-simple-tool-to-test-malloc-performance/
Se desideri compilare manualmente glibc e i suoi benchtest, procedi come segue:
# IMPORTANT: begin AT THE SAME DIRECTORY LEVEL as the `glibc` source code
# directory, NOT inside the `glibc` source code dir! In other words, if
# you are in the correct dir, running `ls` will show the `glibc` source
# code dir (that you just cloned) inside the dir you are in.
mkdir -p glibc-build
mkdir -p glibc-install
cd glibc-build
../glibc/configure --prefix="$(realpath "../glibc-install")"
time make -j8 # build with 8 threads (jobs); on a fast laptop this takes ~3 min.
time make install # (optional: install to the `glibc-install` dir you created)
# Build the benchtests (everything inside the `glibc/benchtests` dir) too;
# see the makefile 'glibc/benchtests/Makefile' for more build commands.
time make bench-build -j8
# Now you have this executable file you can use for malloc speed tests, for instance!:
# ../glibc-build/benchtests/bench-malloc-thread
# To build **and run** all glibc benchtests, do:
time make bench
Riferimenti:
- https://www.gnu.org/software/libc/manual/html_node/Configuring-and-compiling.html
- https://kazoo.ga/a-simple-tool-to-test-malloc-performance/
- https://github.com/f18m/malloc-benchmarks/blob/master/Makefile#L122-L129 :Ho imparato molto di questo studiando questo target makefile:
$(glibc_install_dir)/lib/libc.so.6: @echo "Building GNU libc... go get a cup of coffee... this will take time!" mkdir -p $(glibc_build_dir) cd $(glibc_build_dir) && \ ../glibc/configure --prefix=$(glibc_install_dir) && \ make $(parallel_flags) && \ make install [ -x $(glibc_build_dir)/benchtests/bench-malloc-thread ] && echo "GNU libc benchmarking utility is ready!" || echo "Cannot find GNU libc benchmarking utility! Cannot collect benchmark results"
- Come compilare la mia libreria standard C glibc dai sorgenti e usarla?
Parole chiave:come costruire ed eseguire glibc e i suoi benchtest, incl. benchtest malloc, dalla fonte; costruire glibc dai sorgenti su linux ubuntu
Il Makefile
esisterà nel tuo build-glibc
directory se configure
lo script termina correttamente.
Se tutto sembra essere andato liscio durante configure
e ancora nessun Makefile
, allora probabilmente ti sei perso un'idiosincrasia:
Mentre esegui un configure
per glibc, è previsto che tu fornisca normalmente un --prefix
alternativo , perché l'installazione nella posizione predefinita (/usr/local
) può potenzialmente paralizzare il sistema. Se non ne fornisci uno, devi attivare --disable-sanity-checks
.
Se anche questo non è il caso, cerca un config.log
file e leggerne il contenuto.
Setup 1:glibc senza GCC dedicato
Questa configurazione potrebbe funzionare ed è veloce in quanto non ricompila l'intera toolchain GCC, solo glibc.
L'unico problema che ho con questa configurazione è che non ho trovato un modo carino per usare oggetti di runtime come crt1.o
, crti.o
e crtn.o
forniti dalla nostra glibc, e per ora sto usando quelli dell'host. Questo è menzionato in:https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location Questi oggetti eseguono la configurazione iniziale su cui si basa glibc, quindi non sarei sorpreso se le cose andassero in crash in modo meraviglioso e modi incredibilmente sottili. Vedi i tentativi per risolverlo di seguito.
Compila glibc e installa localmente:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.32
mkdir build
cd build
export glibc_install="$(pwd)/install"
../configure --prefix "$glibc_install"
make -j `nproc`
make install -j `nproc`
Setup 1:verifica la build
test_glibc.c
#define _GNU_SOURCE
#include <assert.h>
#include <gnu/libc-version.h>
#include <stdatomic.h>
#include <stdio.h>
#include <threads.h>
atomic_int acnt;
int cnt;
int f(void* thr_data) {
for(int n = 0; n < 1000; ++n) {
++cnt;
++acnt;
}
return 0;
}
int main(int argc, char **argv) {
/* Basic library version check. */
printf("gnu_get_libc_version() = %s\n", gnu_get_libc_version());
/* Exercise thrd_create from -pthread,
* which is not present in glibc 2.27 in Ubuntu 18.04.
* https://stackoverflow.com/questions/56810/how-do-i-start-threads-in-plain-c/52453291#52453291 */
thrd_t thr[10];
for(int n = 0; n < 10; ++n)
thrd_create(&thr[n], f, NULL);
for(int n = 0; n < 10; ++n)
thrd_join(thr[n], NULL);
printf("The atomic counter is %u\n", acnt);
printf("The non-atomic counter is %u\n", cnt);
}
Compila ed esegui con test_glibc.sh
:
#!/usr/bin/env bash
set -eux
gcc \
-L "${glibc_install}/lib" \
-I "${glibc_install}/include" \
-Wl,--rpath="${glibc_install}/lib" \
-Wl,--dynamic-linker="${glibc_install}/lib/ld-linux-x86-64.so.2" \
-std=c11 \
-o test_glibc.out \
-v \
test_glibc.c \
-pthread \
;
ldd ./test_glibc.out
./test_glibc.out
Comando adattato da https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location
Il programma produce l'atteso:
gnu_get_libc_version() = 2.32
The atomic counter is 10000
The non-atomic counter is 8674
ldd
l'output conferma che ldd
e le librerie che abbiamo appena creato vengono effettivamente utilizzate come previsto:
+ ldd test_glibc.out
linux-vdso.so.1 (0x00007ffe4bfd3000)
libpthread.so.0 => /home/ciro/glibc/build/install/lib/libpthread.so.0 (0x00007fc12ed92000)
libc.so.6 => /home/ciro/glibc/build/install/lib/libc.so.6 (0x00007fc12e9dc000)
/home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)
Il gcc
l'output di debug della compilazione mostra che sono stati utilizzati i miei oggetti di runtime host, il che è negativo come accennato in precedenza, ma non so come aggirarlo, ad es. contiene:
COLLECT_GCC_OPTIONS=/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crt1.o
Setup 1:modifica glibc
Ora modifichiamo glibc con:
diff --git a/nptl/thrd_create.c b/nptl/thrd_create.c
index 113ba0d93e..b00f088abb 100644
--- a/nptl/thrd_create.c
+++ b/nptl/thrd_create.c
@@ -16,11 +16,14 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <stdio.h>
+
#include "thrd_priv.h"
int
thrd_create (thrd_t *thr, thrd_start_t func, void *arg)
{
+ puts("hacked");
_Static_assert (sizeof (thr) == sizeof (pthread_t),
"sizeof (thr) != sizeof (pthread_t)");
Poi ricompila e reinstalla glibc, e ricompila e riesegui il nostro programma:
cd glibc/build
make -j `nproc`
make -j `nproc` install
./test_glibc.sh
e vediamo hacked
stampato alcune volte come previsto.
Ciò conferma ulteriormente che abbiamo effettivamente utilizzato la glibc che abbiamo compilato e non quella dell'host.
Testato su Ubuntu 20.10.
Setup 1:tenta di utilizzare il crt*
corretto oggetti
https://sourceware.org/glibc/wiki/Testing/Builds?action=recall&rev=21#Compile_against_glibc_in_an_installed_location suggerisce di aggiungere --sysroot
al gcc
comando ma:
- in realtà non cambia gli oggetti in nostri secondo i log
- e fa fallire la compilazione con
/usr/bin/ld: cannot find libgcc_s.so.1
presumibilmente perché ilsysroot
viene utilizzato per questo oggetto fornito da GCC, che non abbiamo in quel sysroot perché abbiamo creato solo glibc
Su https://stackoverflow.com/a/66634184/895245 ZeZNiQ fornisce una soluzione probabilmente corretta, passando:
-nostartfiles
seguito da tutti gli oggetti. Devi solo estrarre gli oggetti corretti dal comando completo con -nostartfiles
e passali manualmente.
Ad esempio, sulla mia macchina amd64, gli oggetti utilizzati erano diversi dal suo comando a 32 bit, quindi è un po' complicato.
Bibliografia:
- Come faccio a cambiare la directory di ricerca predefinita di GCC per crti.o?
- https://gcc.gnu.org/legacy-ml/gcc-help/2015-02/msg00016.html
- https://gcc.gnu.org/legacy-ml/gcc-help/2001-11/msg00029.html
Configurazione 2:configurazione originaria di crosstool-NG
Questa è un'alternativa alla configurazione 1, ed è la configurazione più corretta che ho raggiunto finora:tutto è corretto per quanto posso osservare, inclusi gli oggetti di runtime C come crt1.o
, crti.o
e crtn.o
.
In questa configurazione, compileremo una toolchain GCC completamente dedicata che utilizza la glibc che vogliamo.
L'unico aspetto negativo di questo metodo è che la compilazione richiederà più tempo. Ma non rischierei una configurazione di produzione con qualcosa di meno.
crosstool-NG è un set di script che scarica e compila per noi tutto dal sorgente, inclusi GCC, glibc e binutils.
Sì, il sistema di compilazione di GCC è così pessimo che abbiamo bisogno di un progetto separato per questo.
Questa configurazione non è perfetta solo perché crosstool-NG non supporta la compilazione degli eseguibili senza -Wl
extra flags, il che sembra strano dato che abbiamo creato GCC stesso. Ma tutto sembra funzionare, quindi questo è solo un inconveniente.
Ottieni crosstool-NG, configuralo e crealo:
git clone https://github.com/crosstool-ng/crosstool-ng
cd crosstool-ng
git checkout a6580b8e8b55345a5a342b5bd96e42c83e640ac5
export CT_PREFIX="$(pwd)/.build/install"
export PATH="/usr/lib/ccache:${PATH}"
./bootstrap
./configure --enable-local
make -j `nproc`
./ct-ng x86_64-unknown-linux-gnu
./ct-ng menuconfig
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
La compilazione richiede da trenta minuti a due ore.
L'unica opzione di configurazione obbligatoria che posso vedere è farla corrispondere alla versione del kernel dell'host per utilizzare le intestazioni del kernel corrette. Trova la tua versione del kernel host con:
uname -a
che mi mostra:
4.15.0-34-generic
quindi in menuconfig
Sì:
Operating System
Version of linux
quindi seleziono:
4.14.71
che è la prima versione uguale o precedente. Deve essere più vecchio poiché il kernel è compatibile con le versioni precedenti.
Setup 2:configurazioni opzionali
Il .config
che abbiamo generato con ./ct-ng x86_64-unknown-linux-gnu
ha:
CT_GLIBC_V_2_27=y
Per cambiarlo, in menuconfig
fare:
C-library
Version of glibc
salva il .config
e continua con la compilazione.
Oppure, se vuoi usare la tua fonte glibc, ad es. per usare glibc dall'ultimo git, procedi così:
Paths and misc options
Try features marked as EXPERIMENTAL
:impostato su true
C-library
Source of glibc
Custom location
:dire di sìCustom location
Custom source location
:punta a una directory contenente il tuo sorgente di glibc
dove glibc è stato clonato come:
git clone git://sourceware.org/git/glibc.git
cd glibc
git checkout glibc-2.28
Configurazione 2:provalo
Dopo aver creato la toolchain che desideri, provala con:
#!/usr/bin/env bash
set -eux
install_dir="${CT_PREFIX}/x86_64-unknown-linux-gnu"
PATH="${PATH}:${install_dir}/bin" \
x86_64-unknown-linux-gnu-gcc \
-Wl,--dynamic-linker="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib/ld-linux-x86-64.so.2" \
-Wl,--rpath="${install_dir}/x86_64-unknown-linux-gnu/sysroot/lib" \
-v \
-o test_glibc.out \
test_glibc.c \
-pthread \
;
ldd test_glibc.out
./test_glibc.out
Tutto sembra funzionare come in Setup 1, tranne per il fatto che ora sono stati utilizzati gli oggetti di runtime corretti:
COLLECT_GCC_OPTIONS=/home/ciro/crosstool-ng/.build/install/x86_64-unknown-linux-gnu/bin/../x86_64-unknown-linux-gnu/sysroot/usr/lib/../lib64/crt1.o
Setup 2:tentativo di ricompilazione efficiente di glibc fallito
Non sembra possibile con crosstool-NG, come spiegato di seguito.
Se ricostruisci semplicemente;
env -u LD_LIBRARY_PATH time ./ct-ng build CT_JOBS=`nproc`
quindi vengono prese in considerazione le tue modifiche alla posizione di origine della glibc personalizzata, ma crea tutto da zero, rendendolo inutilizzabile per lo sviluppo iterativo.
Se lo facciamo:
./ct-ng list-steps
fornisce una bella panoramica dei passaggi della creazione:
Available build steps, in order:
- companion_tools_for_build
- companion_libs_for_build
- binutils_for_build
- companion_tools_for_host
- companion_libs_for_host
- binutils_for_host
- cc_core_pass_1
- kernel_headers
- libc_start_files
- cc_core_pass_2
- libc
- cc_for_build
- cc_for_host
- libc_post_cc
- companion_libs_for_target
- binutils_for_target
- debug
- test_suite
- finish
Use "<step>" as action to execute only that step.
Use "+<step>" as action to execute up to that step.
Use "<step>+" as action to execute from that step onward.
pertanto, vediamo che ci sono passaggi di glibc intrecciati con diversi passaggi GCC, in particolare libc_start_files
viene prima di cc_core_pass_2
, che è probabilmente il passaggio più costoso insieme a cc_core_pass_1
.
Per costruire un solo passaggio, devi prima impostare "Salva passaggi intermedi" in .config
opzione per la build iniziale:
Paths and misc options
Debug crosstool-NG
Save intermediate steps
e poi puoi provare:
env -u LD_LIBRARY_PATH time ./ct-ng libc+ -j`nproc`
ma sfortunatamente, il +
richiesto come indicato su:https://github.com/crosstool-ng/crosstool-ng/issues/1033#issuecomment-424877536
Si noti tuttavia che il riavvio in un passaggio intermedio reimposta la directory di installazione allo stato che aveva durante tale passaggio. Cioè, avrai una libc ricostruita, ma nessun compilatore finale compilato con questa libc (e quindi, nemmeno librerie di compilatori come libstdc++).
e fondamentalmente rende ancora la ricostruzione troppo lenta per essere fattibile per lo sviluppo, e non vedo come superare questo problema senza patchare crosstool-NG.
Inoltre, partendo dal libc
step non sembrava copiare di nuovo la fonte da Custom source location
, rendendo ulteriormente questo metodo inutilizzabile.
Bonus:stdlibc++
Un bonus se sei interessato anche alla libreria standard C++:come modificare e ricostruire il sorgente della libreria standard C++ GCC libstdc++?
Aggiungendo alla precedente risposta/soluzione di Ciro https://stackoverflow.com/a/52454710/4726668 :
@CiroSantilli La modifica della risposta restituisce "La coda di modifica suggerita è piena". Lo script ldd che stai chiamando nel test_glibc.sh
lo script punta al linker dinamico dell'host:/home/ciro/glibc/build/install/lib/ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fc12f1b3000)
. Per risolvere questo problema, in test_glibc.sh
, cambia ldd
a ${glibc_install}/bin/ldd
. Ciò richiederà l'aggiunta del crt
compilato *.o allo script:
-nostartfiles \
${glibc_install}/lib/crti.o \
${glibc_install}/lib/crtn.o \
${glibc_install}/lib/crt1.o \
Sulla mia macchina GNU/Linux i386/i686 (arch x86 a 32 bit), di seguito è riportato il mio test_glibc.sh
funzionante :
#!/usr/bin/env bash
set -eux
gcc \
-L "${glibc_install}/lib" \
-I "${glibc_install}/include" \
-Wl,--rpath="${glibc_install}/lib" \
-Wl,--dynamic-linker="${glibc_install}/lib/ld-linux.so.2" \
-std=c11 \
-nostartfiles \
${glibc_install}/lib/crti.o \
${glibc_install}/lib/crtn.o \
${glibc_install}/lib/crt1.o \
-o test_glibc.out \
-v \
test_glibc.c \
-pthread \
;
${glibc_install}/bin/ldd ./test_glibc.out
./test_glibc.out