Sviluppo - Come eseguire il debug
TDF LibreOffice Document Liberation Project Blog comunitari Weblate Nextcloud Redmine Ask LibreOffice Donazione
Opzioni per il debug
Dovete abilitare il supporto per il debug al fine di poter eseguire in pratica un debug interattivo. Potete abilitarlo per l'intera compilazione con:
./autogen.sh --enable-debug
oppure
./autogen.sh --enable-dbgutil
Se avete già eseguito una compilazione senza debug, dovrete eseguire make clean
. Se successivamente vorrete tornare ad eseguire compilazioni senza debug, dovrete eseguire nuovamente il clean.
Usare --enable-dbgutil
è come usare --enable-debug
ed in aggiunta abilita delle asserzioni più o meno utili, del codice aggiuntivo di debug ed anche il modo di debug STL di libstdc++ su alcune piattaforme basate su GCC (ma non su macOS in quanto libstdc++ di Apple non lo supporta e libc++ di clang non pare avere un modo di debug) e usa i runtime di debug (compreso il debug STL) con MSVC. Fate attenzione che non è possibile mescolare codice compilato con o senza --enable-dbgutil
.
Fate attenzione che una compilazione completa con --enable-debug
o --enable-dbgutil
per tutti i moduli richiede una grande quantità di spazio su disco.
Se pensate che questo sia troppo potete usare --enable-symbols
per abilitare i simboli di debug solo per specifiche parti,
./autogen.sh --enable-dbgutil --enable-symbols="sw/ sc/ xmloff/"
o eseguire una compilazione completa senza debug e poi ricompilare solo i moduli che vi interessano:
make <module>.clean && make <module> debug=t
Eseguire il debug con un'IDE
Vedete Development/IDE per sapere come impostare le varie IDE per il debug.
Eseguire il debug con gdb
Ci sono due metodi: potete avviare LibreOffice usando lo script dedicato (soffice, swriter, ecc.) e poi agganciare il debugger al processo di LibreOffice, oppure avviare il binario di LibreOffice (soffice.bin
) direttamente dal debugger.
Nelle note seguenti la variabile $LOROOT
è impostata al percorso della cartella nella quale avete clonato il repository core da git, che è il percorso della cartella in cui si trova lo script autogen.sh.
Per strumenti, interfacce grafiche, tutorial e aiuti su gdb, vedete l'elenco completo su cpplinks.
Collegamento al processo soffice.bin
Questo è abbastanza semplice, poiché lo script soffice si prenderà cura di impostare tutte le variabili d'ambiente necessarie:
$ instdir/program/soffice # o /sdraw /swriter ...
$ gdb --pid=$(pidof soffice.bin)
(gdb)
Se l'aggancio fallisce con "ptrace: Operation not permitted", eseguite questo:
sudo su -
echo 0 > /proc/sys/kernel/yama/ptrace_scope
o modificare /etc/sysctl.d/10-ptrace.conf e aggiungete kernel.yama.ptrace_scope = 0
per saltare permanentemente questa restrizione di sicurezza.
Quando eseguite lo script di lancio (soffice, swriter, …) potete passare l'opzione -norestore
: questa sopprimerà il riavvia/ripristino dopo degli errori fatali.
Per agganciare e sganciare un processo dall'interno della shell di gdb potete utilizzare i comandi attach <pid>
e detach
, quest'ultimo non necessita alcun argomento.
Dopo essersi agganciato al processo soffice.bin
gdb termina il programma.
Avvio di LibreOffice dal debugger (gdb)
Il modo più semplice di eseguire LibreOffice dal debuger gdb è eseguire dal livello principale il comando:
make debugrun
Il soffice.bin
avviato ascolterà la pipe con nome, in modo che possiate connettervi questa da un altro processo tramite UNO), oppure eseguirlo direttamente:
(gdb) run --writer
(gdb) run --calc
Funziona anche con MSVC, ma lancerà solamente soffice.bin
e dovrete agganciare manualmente il debugger da Visual Studio.
All'interno di JVM
La JVM userà i segnali di violazione della segmentazione per controllare la presenza di eccezioni di tipo null pointer https://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/signals.html - questi segnali non indicano un crash di LO e si verificano molteplici volte quando la JVM è inizializzata.
Questi segnali possono essere ignorati, ma questo li ignorerà anche se il codice C++ di LibreOffice andasse in crash, perciò usateli con cautela.
(gdb) handle SIGSEGV nostop
In alternativa, disabilitate la JVM in
▸ ▸ ▸ ; è necessaria solo per il debug delle interazioni delle estensioni in Java con LibreOffice.Un'introduzione a gdb
Una volta che il programma è arrestato potete ricercare i simboli, vedere i dati e impostare punti di interruzione:
(gdb) info fun DrawEllipse
All functions matching regular expression "DrawEllipse":
File $LOROOT/svtools/source/filter.vcl/wmf/winmtf.cxx:
void WinMtfOutput::DrawEllipse(Rectangle const&);
File $LOROOT/clone/libs-gui/vcl/source/gdi/outdev5.cxx:
void OutputDevice::DrawEllipse(Rectangle const&);
File $LOROOT/clone/libs-gui/vcl/source/gdi/pdfwriter.cxx:
void vcl::PDFWriter::DrawEllipse(Rectangle const&);
Non-debugging symbols:
0x00007f826dd4a318 OutputDevice::DrawEllipse(Rectangle const&)
0x00007f826dd4a318 [mailto:_ZN12OutputDevice11DrawEllipseERK9Rectangle@plt _ZN12OutputDevice11DrawEllipseERK9Rectangle@plt]
(gdb) break vcl::PDFWriter::DrawEllipse
Breakpoint 1 at 0x7f826c7c5e20: file /opt/shared/work/source_code/libreoffice/libo/clone/libs-gui/vcl/source/gdi/pdfwriter.cxx, line 159.
Ora è impostato un punto di interruzione nella posizione relativa ad un dato metodo. Notate che il nome del metodo deve essere completo e qualificato comprensivo del nome della classe e del namespace di riferimento.
La sintassi generale è: break <location>
, dove <location> può essere il nome completo e qualificato di una funzione o di un metodo, un indirizzo di memoria o il nome di un file seguito da un “:” e un numero di riga. Per maggiori dettagli sul concetto di location leggete la pagina del manuale di gdb: Location specification
Per elencare i punti di interruzione che sono stati impostati potete usare il comando:
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y 0x00007f826c7c5e20 in vcl::PDFWriter::DrawEllipse(Rectangle const&)
at /opt/shared/work/source_code/libreoffice/libo/clone/libsgui/vcl/source/gdi/pdfwriter.cxx:159
Per eliminare un punto di interruzione potete usare uno dei seguenti comandi:
clear <location>
delete <breakpoint numero>
Punti di interruzione in sospeso
Nel caso in cui un determinato simbolo appartenga a una libreria condivisa che non è stata ancora caricata, potete comunque aggiungere un punto di interruzione collegato a quel simbolo, gdb imposterà quello che viene definito un punto di interruzione sospeso che sarà attivato non appena il simbolo sarà disponibile.
(gdb) break SVGActionWriter::ImplWriteRect
Can't find member of namespace, class, struct, or union named "SVGActionWriter::ImplWriteRect"
Hint: try 'SVGActionWriter::ImplWriteRect<TAB> or 'SVGActionWriter::ImplWriteRect<ESC-?>
(Note leading single quote.)
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (SVGActionWriter::ImplWriteRect) pending.
(gdb) info break
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> SVGActionWriter::ImplWriteRect
Se desiderate che gdb imposti automaticamente come punti di interruzione sospesi tutti i simboli sconosciuti senza che ve lo chieda per ognuno, dovete eseguire il seguente comando:
set breakpoint pending on
per ritornare allo stato predefinito impostatelo su “auto”.
Un problema dei punti di interruzione sospesi è che dovete ricordare il nome qualificato completo della funzione o del metodo: il comando info fun
non vi aiuterà in quanto il simbolo è ancora sconosciuto. Una soluzione possibile è fornita dagli eventi degli oggetti condivisi.
Scaricamento di stringhe (ecc)
Basandosi sulla magia delle pretty printer (stampe abbellite) di gdb da gdb 7 in poi print string scaricherà i contenuti delle stringhe indipendentemente che la loro codifica sia UTF-16 rtl::OUString o 8-bit rtl::OString. Esiste il supporto pretty-printer per lo scaricamento corretto di una varietà di altri oggetti oltre alle stringhe, ad es. Any, Sequence, Date, Time, ecc. Usate print/r (o p/r) se avete proprio la necessità di vedere la struttura originale in formato non abbellito. Le stampe abbellite sono attivate automaticamente per il debug in fase di compilazione e per l'esecuzione dalla cartella instdir.
Nota: se le stampe abbellite non funzionano da subito, probabilmente un impostazione di sicurezza di gdb ne impedisce il caricamento; cercate un messaggio di errore che fa riferimento a "safe-path". Per evitare il problema, aggiungete nel file di configurazione $HOME/.gdbinit
:
add-auto-load-safe-path /path/to/your/lo/git # il percorso delle vostre cartelle workdir e instdir
Nota: gdb per Apple è antiquato e non supporta pretty-printer di Python.
Scaricare i container delle STL
GDB può fare le stampe abbellite anche dei contenuti dei container delle STL, il che è molto utile.
Eventi degli oggetti condivisi
Un evento di un oggetto condiviso si verifica ogni volta che una libreria condivisa viene caricata o scaricata. Per impostazione predefinita non sono attivati, ma potete attivarli con il seguente comando:
set stop-on-solib-events 1
Ora ogni volta che una libreria condivisa viene caricata il programma si fermerà e sarete in grado di cercare i nuovi simboli in modo da impostare nuovi punti di interruzione.
Dovreste attivare gli eventi degli oggetti condivisi sono se sapere che la libreria condivisa di vostro interesse, sarà caricata in breve tempo, altrimenti dovrete dare molte volte di comando continue
e il tutto diventerà molto noioso.
Come fare per sapere se la libreria condivisa di vostro interesse è stata caricata? È possibile determinarlo usando il comando info shared <reg expr>
. Questo comando elencherà tutte le librerie caricate che corrispondono a una determinata espressione regolare.
Debug di UNO
Quando cercate il tipo C++ reale di un riferimento UNO potete usare:
(gdb) print *rShape._pInterface
$1 = {_vptr.XInterface = 0x2aaac99f9728 <vtable for SvxShapeText+648>}
(gdb) print rShape._pInterface
$2 = (com::sun::star::uno::XInterface *) 0x2313e9
Misc helper
Alcune estensioni gdb che possono essere utili in certe circostanze:
- info mutex: visualizza quali thread hanno il mutex bloccato
- fcatch: ferma l'esecuzione quando si verifica un eccezione, ma solo se una determinata funzione si trova nello stack
Debug usando DDD (un'interfaccia per gdb)
Se volte collegare il debugger al processo soffice.bin dovete eseguire LibreOffice:
$ cd $LOROOT/install/program
$ ./soffice # o ./swriter, ./simpress, ...
Nel caso desideriate avviare LibreOffice dal debugger dovrete in ogni caso avere nei sorgenti le variabili di ambiente prima di eseguirlo:
$ cd $LOROOT/install/program
$ source ./ooenv
Infine potete avviare DDD con il comando ddd
.
In entrambi i casi dovete impostare il programma di cui effettuare il debug, fate clic sulla voce Open Program
del menu File
. Apparirà una finestra di dialogo: selezionate l'eseguibile soffice.bin
che trovate in $LOROOT/install/program
. Nel caso abbiate già avviato LibreOffice dovete collegare il processo soffice.bin
: potete farlo con la voce di menu File>Attach to process
. Vi si presenterà un elenco dei processi in esecuzione, fate un doppio clic sul processo soffice.bin
(dovrebbe essere già selezionato). Allo stesso modo per scollegare il processo eseguite il comando File>Detach
.
Dopo averlo collegato al processo il programma è sospeso e potete iniziare il suo debug. Il riquadro Command Tool si aprirà automaticamente fornendo un insieme completo di pulsati per le azioni necessarie per riprendere l'esecuzione e per avanzare passo passo nel programma. In basso nella finestra DDD potete vedere la GDB Console: una shell nella quale potete eseguire direttamente dei comandi di gdb. Sopra la GDB Console c'è la Source Window nella quale potete vedere il codice sorgente del programma.
Per modificare il tipo e le dimensioni del carattere della GDB Console e della Source Window dovete andare in Edit>Preferences...>Fonts
e modificare la proprietà Fixed Font
. Al fine di rendere permanenti le vostre personalizzazioni dovete selezionare la voce Save Options
nel menu Edit
.
Per avviare LibreOffice dal debugger potete fare clic sul pulsante Run
(nel riquadro fluttuante Command Tool). Oppure potete dare il comando run
o start
direttamente nella GDB Console, che vi permetterà di passare degli argomenti dalla linea di comando al programma.
Dalla voce di menu Status>Backtrace
aprirete una finestra di dialogo che visualizza una sequenza di chiamate alle funzioni: queste iniziano dalla chiamata alla funzione principale e arrivano all'ultima funzione chiamata: quella dove è stato fermato il programma. Selezionate una chiamata di funzione dall'elenco e nella Source Window sarà visualizzato il codice sorgente nel punto in cui è eseguita la funzione (se viene trovato il file con i sorgenti). Una grande freccia alla sinistra visualizzerà la posizione di esecuzione corrente. Se volete esaminare la backtrace di un thread differente dovete aprire la finestra di dialogo Thread
usando la voce con lo stesso nome nel menu Status
.
Sotto la menu di DDD c'è un campo di modifica con diversi pulsanti alla destra. Potete pensare ad ogni pulsante come ad un'azione che sarà eseguita usando il testo presente nel campo di modifica ogni volta che il pulsante viene premuto. Se scrivete nel campo di modifica il nome di un simbolo pienamente qualificato e poi fate clic sul pulsante Lookup
, il file sorgente in cui è definito quel simbolo file sarà aperto nella finestra Source Window. Nel caso in cui scriviate qualsiasi stringa nel campo di modifica e poi facciate clic sul pulsante Find>>
(forward) la stringa indicata sarà cercata da lì in avanti nel file sorgente corrente.
Quando il simbolo che scrivete nel campo di modifica è una localizzazione valida facendo clic sul pulsante Break
imposterà un punto di interruzione in quella localizzazione. Se aprite il file sorgente che contiene il simbolo in questione, vedrete che una piccola icona "stop" è comparsa a sinistra della riga in cui il simbolo è definito, ad indicare che è stato creato un punto di interruzione. Facendo clic su di esso con il pulsante destro si aprirà un menu contestuale con diverse opzioni. Allo stesso modo facendo clic sul pulsante Watch
verrà definito un punto di osservazione per quel simbolo. Un altro modo per impostare un punto di interruzione è quello di fare clic con il pulsante destro su una linea di codice nella finestra Source Window e selezionare la voce Set Breakpoint
.
Il pulsante Print
stamperà nella shell di gdb il valore del simbolo che avete inserito nel campo di modifica, per i valori più complessi il pulsante Display
visualizzerà il simbolo in modo grafico nella finestra Data Window
che si aprirà automaticamente sopra la finestra Source Window. Invece se posizionate il puntatore del mouse sul simbolo di una variabile nella finestra Source Window il suo valore sarà automaticamente visualizzato in un piccolo riquadro di suggerimento. Nella finestra Data Window potete far visualizzare anche tutte le variabili locali disponibili e gli argomenti passati alla funzione corrente: per renderli visibili dovete selezionare le voci corrispondenti nel menu Data
. Ogni riquadro di dati visualizzato nella finestra Data Window viene chiamato Display
. Poteste spostarlo all'interno della Data Window a vostro piacimento, inoltre quando ci fate clic sopra con il pulsante destro appare un menu contestuale che presenta diverse voci relative alle impostazioni.
Punti di interruzione in sospeso
I punti di interruzione in sospeso non sono supportati da DDD. Anche se eseguite il comando break <simbolo ancora sconosciuto>
direttamente nella Console di GDB Console, non vi sarà presentata alcuna richiesta di aggiungerlo come punto di interruzione in sospeso. Un modo per aggirare il problema è quello di impostare la proprietà breakpoint pending
su on (l'impostazione predefinita è auto, potete farlo eseguendo il comando set breakpoint pending on
nella Console di GDB Console o controllando l'opzione corrispondente nella finestra di dialogo GDB Settings
(Edit>GDB Settings ...
).
Ora il pulsante Break
può impostare qualsiasi stringa come punto di interruzione (in sospeso o no). Fate comunque attenzione! C'è un inconveniente subdolo. Il primo punto di interruzione non in sospeso che create dopo averne impostato uno in sospeso, viene messo in ombra da quest'ultimo. Aprendo la finestra di dialogo Breackpoints
(Source>Breakpoints...
) vedrete che il punto di interruzione non sospeso non può essere eliminato e le sue proprietà non possono essere impostate. Ogni azione che eseguite sul punto di interruzione non in sospeso, anche tramite il menu contestuale breakpoint nella Source Window, verrà eseguita su quello in sospeso.
In ogni caso non confondetevi, questo è solo un problema dell'interfaccia DDD: anche se è messo in ombra il punto di interruzione non in sospeso è impostato e il programma si fermerà quando lo raggiunge e in ogni caso può essere gestito direttamente tramite la Console di GDB. Quando il punto di interruzione in sospeso viene eliminato quello non in sospeso può essere gestito anche tramite l'interfaccia DDD.
Eventi degli oggetti condivisi
Per abilitare gli eventi degli oggetti condivisi potete dare il comando set stop-on-solib-events 1
nella Console di GDB Console o controllare la voce Stopping for Shared Library Events
nella finestra di dialogo GDB Settings
(Edit>GDB Setting...
)
Apertura dei file sorgenti
Potete cercare i file sorgenti tramite la finestra di dialogo Open Source
( ▸ ): qui, nel campo modificabile Filter
potete inserire una stringa es.: "*main*"
(si applicano le regole dei pattern glob) e tutti i file che contengono un simbolo conosciuto saranno confrontati con questa stringa quando farete clic sul pulsante Filter
posizionato in basso. Per maggiori dettagli sulla funzionalità della finestra di dialogo Open Source fate clic sul pulsante Help
.
Eseguire il debug con lldb
Se non avete a disposizione gdb, diciamo perché usate macOS, allora dovete usare lldb.
I comandi sono diversi tra gdb e lldb.
Esistono alcune utili stampe abbellite in python che potete caricare manualmente per stampare OUStrings ecc., vedete solenv/lldb/libreoffice/LO.py
.
Sfortunatamente almeno lldb per Apple sembra avere la cattiva abitudine di troncare gli argomenti della linea di comando, perciò l'avvio dei test CppUnit nel modo abituale può fallire con un eccezione del cppuhelper::ServiceManager::readRdbFile
.
Trovare un elenco delle eccezioni C++ ignorate
Mettiamo che la vostra compilazione di debug restituisca
warn:linguistic:67283:363302:linguistic/source/gciterator.cxx:679: GrammarCheckingIterator::DequeueAndCheck ignoring N3com3sun4star3uno9ExceptionE msg: C++ code threw St13runtime_error: collate_byname<char>::collate_byname failed to construct for
e vogliate avere un elenco delle eccezioni. Il numero di riga qui sopra fa parte del codice che intercetta tutte le eccezioni, ma desiderate un elenco di quei std::runtime_error. Ciò che potete fare è
- aggiungere un punto di interruzione all'inizio del blocco di
try
, in questo caso la riga 608 - una volta che si interrompe in quel punto, rimuovere il punto di interruzione e aggiungerne uno per ogni eccezione di C++
- continuare fino a ottenere un'eccezione C++, quindi stampare l'elenco (possibilmente ripetendo fino a quando non ottenete quello giusto)
Per farlo, per prima cosa avviate il debugger lldb (in $LODE_HOME/dev/core se controllate tramite lode) con
make debugrun
poi quando lldb si è avviato, aggiungete il punto di interruzione iniziale e avviate il programma
(lldb) b linguistic/source/gciterator.cxx:608
# ignorate alcuni avvertimenti in merito al percorso non ancora conosciuto
(lldb) run --writer --norestore /more/arguments/to/soffice
quindi rimuovete il punto di interruzione e cominciate ad interrompere a tutte le eccezioni di C++:
(lldb) break set -E cxx
# facoltativamente cancellate quella vecchia, es. "1" nell'elenco:
(lldb) break list
(lldb) break delete 1
# continuate e risalite a ritroso l'elenco quando vedete un'eccezione
(lldb) c
(lldb) bt
# ripetete fino a che non avrete trovato quella giusta.
Eseguire il debug con WinDbg o Visual Studio (su Windows)
Eseguire il debug con Visual Studio è estremamente facile, è sufficiente una sola compilazione di debug, non è necessario usare l'integrazione con l'IDE. Semplicemente aprite in Visual Studio il file sorgente di interesse, aggiungete un punto di interruzione, avviate soffice.exe
e collegate il debugger a soffice.bin
tramite ▸ . L'esecuzione si fermerà quanto il punto di interruzione sarà raggiunto (per fermare l'esecuzione immediatamente, usate ▸ ). Durante il debug Visual Studio apre automaticamente i file sorgenti del codice eseguito. L'elenco delle chiamate è disponibile nella finestra di nome Call stack.
Miglioramento dell'esperienza di debug con Visual Studio
Ci sono molte opzioni di configurazione in Visual Studio che possono migliorare l'esperienza e la produttività. Le più rilevanti sono le visualizzazioni natvis. Nella cartella /solenv/vs si trova un file .natvis personalizzato, che consente di visualizzare più facilmente gli oggetti di alcune classi usate nel codice di LO. I contenuti del file sono incorporati nei PDB delle compilazioni di debug, perciò il debug usa le visualizzazioni anche quando non usate l'integrazione con l'IDE (file di soluzione generati). Apportate a quel file le aggiunte che ritenete utili per le visualizzazioni ed effettuate il submit delle vostre patch :)
Altri elementi utili: evitare di entrare nelle funzioni che sono semplici e di una riga, come l'operatore -> per gli smart pointer. Qui trovate l'howto: Come *non* entrate in certe funzioni? (Stack Overflow)
Spesso è utile *disabilitare* la funzionalità "Just My Code" del debugger di Visual Studio: Options->Debugger->General; trovate e deselezionate la voce "Enable Just My Code". Altrimenti gli elenchi delle chiamate sarebbero incompleti e inutili.
Eseguire il debug delle compilazioni per il rilascio
In Visual Studio l'elenco delle chiamate in un primo momento può sembrare inutile; per ovviare a questo abilitate il Microsoft Symbol Server nella configurazione:
▸ ▸ ▸ e fate clic su "Microsoft Symbol Servers".Per informazioni su come impostare WinDbg o Visual Studio per il debug delle compilazioni di rilascio di TDF con i simboli scaricati vedete Come ottenere una backtrace con WinDbg.
Registrare una compilazione come server provider COM
Se dovete eseguire il debug dei componenti COM del processo, potete registrare la vostra compilazione come server provider COM usando il seguente script:
REM usage:
REM libreoffice-registry-set-com-path.bat soffice_exe_path
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4} /ve /t REG_SZ /d "LibreOffice Service Manager (Ver 1.0)" /f
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4} /v AppID /t REG_SZ /d "{82154420-0FBF-11d4-8313-005004526AB4}" /f
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4}\LocalServer32 /ve /t REG_EXPAND_SZ /d "%* --nodefault --nologo" /f
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4}\NotInsertable /ve /t REG_SZ /d "" /f
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4}\ProgID /ve /t REG_SZ /d "com.sun.star.ServiceManager.1" /f
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4}\Programmable /ve /t REG_SZ /d "" /f
reg add HKEY_CLASSES_ROOT\CLSID\{82154420-0FBF-11d4-8313-005004526AB4}\VersionIndependentProgID /ve /t REG_SZ /d "com.sun.star.ServiceManager" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager /ve /t REG_SZ /d "LibreOffice Service Manager" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager\CLSID /ve /t REG_SZ /d "{82154420-0FBF-11d4-8313-005004526AB4}" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager\CurVer /ve /t REG_SZ /d "com.sun.star.ServiceManager.1" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager\NotInsertable /ve /t REG_SZ /d "" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager.1 /ve /t REG_SZ /d "LibreOffice Service Manager (Ver 1.0)" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager.1\CLSID /ve /t REG_SZ /d "{82154420-0FBF-11d4-8313-005004526AB4}" /f
reg add HKEY_CLASSES_ROOT\com.sun.star.ServiceManager.1\NotInsertable /ve /t REG_SZ /d "" /f
L'argomento del percorso che punta al file bat deve essere un percorso di tipo Windows, perciò se lo chiamate da una shell di cygwin, dovete usare qualcosa di simile a cygpath -w -a -l ./instdir/program/soffice.exe.
Eseguire il debug degli strumenti di compilazione
Nel caso in cui la compilazione si interrompa quando viene invocato uno strumento personalizzato e compilato durante il processo di compilazione, è probabile che ci sia un bug in qualche punto del codice di quello strumento. Potete usare la variabile d'ambiente BUILDTOOLTRACE
per eseguire la compilazione dello strumento in strace, valgrind o in un debugger:
make BUILDTOOLTRACE='strace' PARALLELISM=1 # esecuzione in strace
make BUILDTOOLTRACE='gdb --args' PARALLELISM=1 # debug con gdb
make BUILDTOOLTRACE='$(DEVENV) /debugexe' PARALLELISM=1 # debug con Visual Studio
Esecuzione dei test cppunit
Per eseguire il test foo di cppunnit nel modulo bar, date:
cd bar && make CppunitTest_foo
La parte foo è ciò che viene visualizzato in un log di compilazione non verboso e il suo primo componente di solito è il modulo (la parte bar); altrimenti eseguite grep foo */*.mk
per trovarla.
Es. il test viene eseguito se il sistema di compilazione stampa
[build CUT] sw_subsequent_ooxmlexport
può essere eseguito manualmente con
cd sw && make CppunitTest_sw_subsequent_ooxmlexport
Debug dei test cppunit
In fase di compilazione, se un test cppunit va in crash potete inserire un debugger con...
$ export CPPUNITTRACE="gdb --args"
ora quando eseguite la compilazione (forzando una compilazione non parallela con -j1), gdb inizierà con il test cppunit caricato, digitate "run" per eseguire il test sotto gdb. Questa opzione si chiamava GDBCPPUNITTRACE fino a ottobre 2013
Su Windows potete avviare il test unit in Visual studio con:
$ export CPPUNITTRACE="\"percorso_per_devenv.exe\" /debugexe"
dove percorso_per_devenv.exe è il percorso assoluto + devenv.exe. Questo avvierà Visual studio e il test partirà direttamente nella UI.
Su macOS non avete più accesso a gdb che è stato sostituito da lldb. Comunque potete usare ancora CPPUNITTRACE tramite
$ export CPPUNITTRACE="lldb --"
In alternativa, se il test va in crash segnalando un'eccezione non intercettata potete tracciare un test cppunit per trovare il punto in cui è stata scartata l'ultima eccezione con
$ export DEBUGCPPUNIT=TRUE
il che registrerà gli scarti e le intercettazioni nel file gdbtrace.log.
Potete eseguire uno specifico metodo per il test del caso all'interno del target di CppUnit make (che normalmente contiene numerosi metodi di test individuali) usando
$ CPPUNIT_TEST_NAME=testFDO76163 make CppunitTest_foo
Eseguire il debug di test cppunit con strace
Se le backtraces di gdb non sono utili, potete provare una strace con
$ make CppunitTest_Nome_Test CPPUNITTRACE="strace -f -s 77" 2>&1 | tee strace.log
Il parametro -s il limite del numero di caratteri a 77
Eseguire il debug di perfcheck di di altri test di cppunit che eseguono valgrind
Per i test delle prestazioni (make perfcheck) o per quelli eseguiti sotto valgrind (vedete i praragragi successivi) non è possibile fare il debug direttamente. Comunque valgrind comprende un gdbserver e il test può essere eseguiti dopo l'esportazione
$ export VALGRIND_GDB=TRUE
quindi lanciare il test normalmente e avviare gdb con
$ gdb workdir/LinkTarget/Executable/cppunittester
e nel prompt di gdb
$ target remote | vgdb
Eseguire il debug degli eseguibili
LibreOffice incorpora un certo numero di eseguibili dimostrativi nel modulo VCL. Per eseguire il debug di questi, dovete prima impostare la variabile d'ambiente LOTRACE in modo che punti al vostro debugger. Su Linux, questa è:
$ export LOTRACE="gdb --args"
Su Windows potete avviare l'eseguibile in Visual studio con:
$ export LOTRACE="\"percorso_per_devenv.exe\" /debugexe"
dove percorso_per_devenv.exe è il percorso assoluto + devenv.exe. Questo avvierà Visual studio e il test partirà direttamente nella UI.
Su macOS non avete più accesso a gdb che è stato sostituito da lldb. Comunque potete usare ancora LOTRACE tramite
$ export LOTRACE="lldb --"
Per eseguire l'app, usate lo script bin/run:
$ bin/run vcldemo
Controllare con valgrind (memcheck) i cppunit test
In fase i compilazione, potete eseguire il memcheck dei test cppunit e di altri test con
$ export VALGRIND=memcheck
Questo imposterà automaticamente G_SLICE=always-malloc e dovrebbe far si che i test cppunit e i testi di regressione di hunspell siano eseguiti sotto valgrind --tool=memcheck
Controllare con valgrind (memcheck) LibreOffice stesso
$ export VALGRIND=memcheck
Questo imposterà automaticamente G_SLICE=always-malloc e dovrebbe far si che lo stesso LibreOffice sia eseguito sotto valgrind con --tool=memcheck
Controllare con valgrind (helgrind) i test cppunit
In fase i compilazione, potete eseguire il controllo helgrind dei test cppunit e di altri test con
$ export VALGRIND=helgrind
Controllare con valgrind (helgrind) LibreOffice stesso
$ export VALGRIND=helgrind
Questo farà si che LibreOffice stesso sia eseguito sotto valgrind con --tool=helgrind
Eseguire i test subsequent
Un make check
a livello principale eseguirà prima una compilazione completa e poi tutti i test subsequent, invece un "make subsequentcheck" a livello principale eseguirà solamente tutti i test subsequent.
Potete fare questo anche dall'interno di un modulo, o solamente su un modulo.
Potete eseguire un singolo test subsequent tramite il suo obiettivo (guardate in foo/Module_foo.mk), es. cd sw && make -rs JunitTest_sw_complex
. La parte cd foo
non è necessaria, ma velocizza il processo.
Se i test falliscono, può essere dovuto all'impostazione locale diversa da "en-US". In questo caso, eseguite un "export LANG=C" e riprovate.
Eseguire il debug dei test subsequent
Ci sarà un file di registro dei test falliti, potete visualizzarlo con un edito di testo:
workdir/JunitTest/<module>_<complex|unoapi>/done.log
Il file di registro contiene una stack trace Java dei test falliti e se soffice.bin
è andato in crash ed ha lasciato un file core, potrete vedere anche una stack trace C++ dei crash.
Se non ci sono crash, un'occhiata alla stack trace Java dovrebbe indicarvi il test Java del codice che è fallito; le parti più interessanti di solito sono nelle classi complex.<module>..."
ed il codice relativo si trova in <module>/qa/complex/
. Questo dovrebbe indicarvi qualche metodo interessante delle API UNO chiamato dalla parte in C++ di soffice.bin
.
Ora per eseguire il debug, questo avvierà l'installazione da
instdir
all'interno di gdb:
make debugrun
Prima fate scomparire l'orribile gdb TUI con "C-x a", poi impostate un punto di interruzione in corrispondenza del metodo difettoso ed eseguite, dovrebbe comparire il centro di avvio.
Quindi potete eseguire (in un altro terminale):
make gb_JunitTest_DEBUGRUN=T <module>.subsequentcheck
Questo eseguirà nuovamente il test sul <soffice>soffice.bin in esecuzione e si spera che raggiunga il punto di interruzione, in modo che possiate fare il debug del problema da quel punto.
Eseguire il debug dei test subsequent di qadevOOo/unoapi
Per i test qadevOOo/unoapi è piuttosto difficili fare il debug.
Se un test fallisce, per prima cosa dovete capire relativamente a quale metodo si lamenta, aprite questo file in un editor di testo:
workdir/JunitTest/<module>_unoapi/done.log
Alla fine c'è il seguente sommario, che indica il componente del test che ha causato il fallimento:
Failures that appeared during scenario execution:
toolkit.AccessibleStatusBarItem
1 of 53 tests failed
Cercate il termine FAIL. Questo mostra un errore del tipo:
LOG> getCharacterBounds(6)
LOG> Text at this place:
LOG> Character bounds outside component
LOG> Character rect: 43, -566, 0, 0
LOG> Component rect: 91, 2, 71, 18
Method getCharacterBounds() finished with state FAILED
LOG> getCharacterBounds(): PASSED.FAILED
Se scorrete un po' verso l'altro vedrete una riga come questa:
checking: [toolkit.AccessibleStatusBarItem::com::sun::star::accessibility::XAccessibleText] is iface: [com.sun.star.accessibility.XAccessibleText] testcode: [ifc.accessibility._XAccessibleText]
Questa punta al codice in Java del test eseguito in questo punto, ifc.accessibility._XAccessibleText
, corrispondente a qadevOOo/tests/java/ifc/accessibility/_XAccessibleText.java
.
C'è anche il codice delle impostazioni di Java specifico per il componente sottoposto al test toolkit.AccessibleStatusBarItem
e si trova in qadevOOo/tests/java/mod/_toolkit/AccessibleStatusBarItem.java
.
Ora riducete un po' il test per provarlo più velocemente: modificate il file di scenario corrispondente, di solito denominato <module>/qa/unoapi/<module>.sce
e rimuovete tutto tranne la riga che corrisponde al test fallito, in questo caso AccessibleStatusBarItem
e controllate se fallisce ancora:
A questo punto la parte difficile in questo caso è trovare dove è implementato il metodo che fallisce;
spesso (ad es. in Writer) la classe avrà quasi lo stesso nome del componente sottoposto al test, ma in questo esempio checkCharacterBounds
sorprendentemente in realtà non è nel modulo del toolkit, ma git grep
punta a accessibility/source/standard/vclxaccessiblestatusbaritem.cxx
,
che contiene un metodo VCLXAccessibleStatusBarItem::getCharacterBounds
.
Una volta trovata questa informazione, procedete con make debugrun
ecc. come descritto nella sezione precedente #Eseguire il debug dei test subsequent.
Eseguire il debug con rr
rr funziona bene per il debug di LibreOffice, anche con elementi ricercati come della JVM in-process. Fate attenzione che rr attualmente richiede Linux e una CPU Intel recente.
Per registrare LO stesso, usate:
rr record instdir/program/soffice
(Nota: per evitare dei crash durante l'uso di versioni di rr fino alla 5.3.0 con versioni recenti di libc++, impostare la variabile d'ambiente SAL_RAND_REPEATABLE=1
(vedete la sezione Variabili d'ambiente) può servire ad aggirare il problema (risolto in https://github.com/mozilla/rr/commit/862605a8d4abca6d28d2296ccc6d6148ffc93ff6 ).
Per eseguire nuovamente, dovrete iniziare dal processo soffice.bin:
rr replay -p $(rr ps | grep soffice.bin | cut -f 1 | tail -n 1)
Nella versione master attuale verso libreoffice-6-2, tutti i test (CppUnitTest, JUnitTest, PythonTest, UITest) possono essere registrati impostando la variabile d'ambiente RR=1
. Questo richiede ~35GB di spazio affinché make check
sia eseguito.
Ci sono stati dei casi che hanno avuto successo nel fare usare rr come debugger a Eclipse CDT 9.4.3, seguendo le istruzioni nella documentazione di rr, usando una configurazione di debug derivata da "C/C++ Application" (e con il percorso completo di soffice.bin nel campo Application), ma con questo script piuttosto avanzato come wrapper per rrgdb:
#!/bin/bash
dir=/home/foobar/.local/share/rr/latest-trace/
pid=$(rr ps $dir | grep soffice.bin | cut -f 1 | tail -n 1)
exec rr replay -p $pid $dir -- "$@"
Per far si che Qt Creator (provato con la versione 4.10 release candidate) trovi le informazioni di debug, la sysroot per GDB deve essere impostata esplicitamente, questo può essere ottenuto per esempio aggiungendo la seguente riga in "Tools" -> "Options" -> "Debugger" -> "GDB" -> "Additional Startup Commands":
set sysroot /
Poi seguite le istruzioni nella documentazione di rr.
Cercare una memoria danneggiata in Windows usando DrMemory
Se avete un bug che causa dei crash su Windows e la stack trace è dentro 'malloc', 'free', 'new' o 'delete' probabilmente avete un caso di memoria corrotta - spesso anche i bug intermittenti sono dovuti a questo. In questi casi, esiste un nuovo strumento meraviglioso (per Windows), che si chiama DrMemory; potete ottenerlo qui: http://www.drmemory.org/
Dovete installarlo ed abilitare il suo inserimento nel path di sistema. Una volta fatto scaricate un versione compilata da TDF: https://download.documentfoundation.org/libreoffice/stable/ o una compilazione giornaliera dall'archivio di debug TB39: https://dev-builds.libreoffice.org/daily/master/Win-x86@39/
Quindi dovrete rinominare il file soffice.bin in soffice.exe all'interno della cartella program/ di LibreOffice. Il file soffice.exe originale è solamente un semplice binario che fa da wrapper.
Infine avrete bisogno di una console di qualche tipo; per ora, al fine di ottenere qualcosa di significativo dallo strumento, dovrete eseguire quanto segue dall'interno della cartella program/ di LibreOffice:
drmemory -no_count_leaks -ignore_asserts -no_check_uninitialized -- soffice.exe
Ciò significa che sarete arrivati piuttosto in là, auspicabilmente fino al punto in cui il bug causa il crash. Dato che il selettore dei file manda in crash lo stesso drmemory, dovrete usare i 'file recenti' o la riga di comando per essere in grado di caricare il vostro documento.
Aspettatevi che sia molto lento; è normale :-) ma sta eseguendo delle cose intelligenti. Auspicabilmente alla fine della giornata, il bug produrrà un:
Error #7: UNADDRESSABLE ACCESS: writing 0x2b9ca0f4-0x2b9ca0f8 4 byte(s)
log degli errori, che è un errore serio e una traccia utile su di questo.
Eseguire i test CppUnit con DrMemory
Potete eseguire qualunque test CppUnit con DrMemory in modo da rilevare i bug di accesso non inizializzato alla memoria e di gestione della memoria, in questo modo:
CPPUNITTRACE="drmemory -no_check_gdi -free_max_frames 30 -suppress C:/Users/xxx/drmemory-suppressions.txt" make CppunitTest_sw_uiwriter
Fate attenzione che:
- DrMemory tende a esaurire la memoria e terminare da solo quando segnala ripetute perdite di memoria all'interno dei test CppUnit di dimensioni maggiori nelle compilazioni a 32-bit; per evitarlo usate gli argomenti
-no_count_leaks -no_check_handle_leaks
.
- DrMemory tende a macinare fino all'arresto (o almeno, con la versione 1.9.0-4, sono state osservate 2 ore di lavoro continuo della CPU prima che si esaurisca la pazienza) quando esegue
java.exe
, ciò è dovuto a diversi test di unità, in particolare a CppunitTest_dbaccess_hsqldb_test, CppunitTest_dbaccess_RowSetClones e CppunitTest_services. Per aggirare questo problema, potete usare-no_follow_children
, o configurare DrMemory in modo da ignorarejava.exe
eseguendodrconfig.exe -quiet -reg java.exe -norun
. (Stranamente, il processo internojvm.dll
è meno problematico: si generano milioni di errori, ma questi possono essere facilmente soppressi, come descritto in seguito.)
- DrMemory non possiede un equivalente del memchck di valgrind
--track-origins=yes
, perciò risalire alla causa che dà origine all'accesso non inizializzato alla memoria spesso comporta del lavoro aggiuntivo; in questi casi verificare se riuscite a riprodurre il problema con valgrind su un'altra piattaforma in modo da ottenere una stack trace migliore.
- DrMemory segnala dei falsi positivi nelle JPEG immagini importate dal codice SSE2 in jpeg-turbo https://github.com/DynamoRIO/drmemory/issues/540. Sfortunatamente potrebbe farlo in punti molto distanti dal filtro di importazione JPEG, per esempio in CppunitTest_sw_globalfilter gli errori UNINITIALIZED READ vengono segnalati quando si esporta una VCL Bitmap in PNG. Il modo di aggirare questo problema è forzare jpeg-turbo a non usare SSE2 impostando la variabile d'ambiente:
export JSIMD_FORCEMMX=1
.
- DrMemory segnale diversi altri falsi positivi che possono essere soppressi tramite l'argomento
-suppress
.
Questo è un esempio drmemory-suppressions.txt
per i falsi positivi incontrati eseguendo CppunitTests con DrMemory-1.9.0-4:
UNADDRESSABLE ACCESS name=suppress all UA in java.exe java.exe!* UNINITIALIZED READ name=suppress all UR in java.exe java.exe!* UNADDRESSABLE ACCESS name=suppress all UA in jvm.dll jvm.dll!* UNINITIALIZED READ name=suppress all UR in jvm.dll jvm.dll!* WARNING name=suppress all warning in jvm.dll jvm.dll!* UNADDRESSABLE ACCESS name=UA in JIT code from jvm.dll <not in a module> ... jvm.dll!* UNINITIALIZED READ name=UR in JIT code from jvm.dll <not in a module> ... jvm.dll!* INVALID HEAP ARGUMENT name=https://connect.microsoft.com/VisualStudio/feedback/details/750951/std-locale-implementation-in-crt-assumes-all-facets-to-be-allocated-on-crt-heap-and-crashes-in-destructor-in-debug-mode-if-a-facet-was-allocated-by-a-custom-allocator drmemorylib.dll!replace_free *!std::_DebugHeapDelete<> *!std::_Fac_node::~_Fac_node *!std::_Fac_node::`scalar deleting destructor' *!std::_DebugHeapDelete<> *!std::_Fac_tidy_reg_t::~_Fac_tidy_reg_t *!std::`dynamic atexit destructor for '_Fac_tidy_reg *!_CRT_INIT *!__DllMainCRTStartup *!_DllMainCRTStartup ntdll.dll!RtlQueryEnvironmentVariable ntdll.dll!LdrShutdownProcess ntdll.dll!RtlExitUserProcess KERNEL32.dll!ExitProcess UNINITIALIZED READ name=https://github.com/DynamoRIO/drmemory/issues/1824 (input UR) system call NtUserGetClipboardFormatName UNICODE_STRING.MaximumLength sysdtrans.dll!CDataFormatTranslator::getClipboardFormatName UNADDRESSABLE ACCESS name=https://github.com/DynamoRIO/drmemory/issues/1824 (input UA) system call NtUserGetClipboardFormatName UNICODE_STRING content sysdtrans.dll!CDataFormatTranslator::getClipboardFormatName UNINITIALIZED READ name=https://github.com/DynamoRIO/drmemory/issues/1824 (output1) sal3.dll!* sal3.dll!rtl_ustr_compareIgnoreAsciiCase_WithLength ftransl.dll!rtl::OUString::equalsIgnoreAsciiCase ftransl.dll!CDataFormatTranslator::findDataFlavorForNativeFormatName ftransl.dll!CDataFormatTranslator::getDataFlavorFromSystemDataType sysdtrans.dll!CDataFormatTranslator::getDataFlavorFromFormatEtc sysdtrans.dll!CDOTransferable::formatEtcToDataFlavor sysdtrans.dll!CDOTransferable::initFlavorList sysdtrans.dll!CDTransObjFactory::createTransferableFromDataObj UNINITIALIZED READ name=https://github.com/DynamoRIO/drmemory/issues/1824 (output2) sal3.dll!rtl::compareIgnoreAsciiCase sal3.dll!rtl_ustr_compareIgnoreAsciiCase_WithLength sysdtrans.dll!rtl::OUString::equalsIgnoreAsciiCase sysdtrans.dll!CDataFormatTranslator::isTextHtmlFormat UNINITIALIZED READ name=https://github.com/DynamoRIO/drmemory/issues/1825 system call NtGdiAddFontResourceW parameter value #4 GDI32.dll!GdiAddFontResourceW GDI32.dll!AddFontResourceExW vcllo.dll!ImplAddTempFont vcllo.dll!WinSalGraphics::AddTempDevFont vcllo.dll!OutputDevice::AddTempDevFont UNINITIALIZED READ name=https://github.com/DynamoRIO/drmemory/issues/1827 * KERNELBASE.dll!WaitNamedPipeW sal3.dll!osl_createPipe UNINITIALIZED READ name=CPython custom allocator PyObject_Realloc python??_d.dll!PyObject_Realloc UNINITIALIZED READ name=CPython custom allocator PyObject_Free python??_d.dll!PyObject_Free UNINITIALIZED READ name=CPython custom allocator PyObject_Realloc python??.dll!PyObject_Realloc UNINITIALIZED READ name=CPython custom allocator PyObject_Free python??.dll!PyObject_Free WARNING name=prefetching unaddressable memory in jpeg-turbo vcllo.dll!jsimd_idct_islow_sse2 vcllo.dll!jsimd_idct_islow vcllo.dll!decompress_data
Eseguire il debug dei cicli di vita degli oggetti UNO in C++
Il contattore di riferimento è memorizzato come m_refCount, quindi, per es., interrompete il costruttore dell'oggetto uno e controllate se vedete quali elementi prendono il possesso condiviso dell'oggetto.
(gdb) watch * (&m_refCount)
bin/refcount_leak.py
Se le chiamate acquire()
e release()
ad un servizio UNO con combaciano, l'oggetto sarà perso.
Esiste uno script che può eseguire il parse delle backtrace di gdb e provare a bilanciare acquire()
e release()
ed riordinarle nel modo in cui probabilmente sono effettivamente.
Per consigli sul suo utilizzo vedete i commenti all'inizio di bin/refcount_leak.py
nel repository core.
Fate attenzione che gdb richiede molto tempo per stampare le backtrace; 4000 backtrace impiegano > 3 ore su di un laptop con una CPU con 15W di TDP.
Un altro inconveniente è che il risultato dello script richiedete alcune interpretazioni manuali, ma può rilevare le sole chiamate a acquire()
mancanti.
strumento uno::Reference
Esiste una patch su gerrit che aggiunge un'allocazione di memoria fittizia in ogni uno::Reference
in modo che gli strumenti standard come valgrind e address sanitizer possano rilevare quando lo stesso uno::Reference
è mancante.
Questo può essere il modo più semplice per rintracciare un leak, ma gli svantaggi sono che non può rilevare le semplici chiamate ad acquire()
e che la strumentazione richiede una ricompilazione completa; inoltre il blocco globale aggiunto può causare dei deadlock con configmgr.
Fate attenzione che la patch attualmente è incompleta e potrebbe non rilevare dei leak da uno::Any e rtl::Reference (ma questo problema può essere corretto).
Asserzioni e Log
Vedete Development/GeneralProgrammingGuidelines#Asserzioni e Log.
Variabili d'ambiente
Queste variabili d'ambiente sono utili per scopi di debug:
OOO_DISABLE_RECOVERY=1
disabilita il ripristino dei documenti corrotti all'avvio.OOO_EXIT_POST_STARTUP=1
esce subito dopo l'apertura del documento. Utile per il debug delle prestazioni in fase di apetura.SAL_DISABLEGL=1
disabilita l'uso di OpenGLSAL_DISABLE_OPENCL=1
disabilita l'uso di OpenCL in Calc.SAL_NO_MOUSEGRABS=1
impedisce a LibreOffice di catturare il mouse durante il debug su X11.SAL_RAND_REPEATABLE=1
fa partire il generatore di numeri casuali da un seme predefinito, che rende prevedibili i test che usano numeri casuali.SAL_USE_VCLPLUGIN=gen/kf5/gtk3
forza l'uso di uno specifico backend per l'interfaccia di VCL.SW_DEBUG=1
abilita le combinazioni di tasti per lo scarico nei documenti di writer (F12 per layout.xml, Shift+F12 per nodes.xml)SD_DEBUG=1
abilita la combinazione di tasti per lo scarico degli oggetti grafici di draw (F12 per model.xml)
Macro per il controllo del debug del codice
- La macro
NDEBUG
è il modo standard per controllare la funzionalità standardassert
. È definita nelle compilazioni destinate alla produzione, lasciatela non definita--enable-debug
/--enable-dbgutil
(fate attenzione che definendola si disabilitano le asserzioni).
- Le macro
SAL_LOG_INFO
eSAL_LOG_WARN
controllano se le rispettive funzionalitàSAL_INFO
eSAL_WARN
, dainclude/sal/log.hxx
sono attivate. Queste non sono definite nelle compilazioni di produzione, definitele con--enable-debug
/--enable-dbgutil
. Se sono attive, il loro comportamento in esecuzione è controllato dalla variabile d'ambienteSAL_LOG
(vedete la documentazione ininclude/sal/log.hxx
per i dettagli).
Per abilitare SAL_INFO
e SAL_WARN
per il componente sw
digitate:
export SAL_LOG="+INFO.sw.ww8+WARN"
- La macro
DBG_UTIL
abilita del codice aggiuntivo che potenzialmente influenza la compatibilità ABI, modificando le strutture di dati pubbliche. (Perciò la sua abilitazione è una decisione "tutto o niente"; di solito non è possibile compilare solo una parte di LibreOffice con questa abilitata. Per ragioni storiche, controlla anche le macro obsoleteDBG_ASSERT
ecc. dainclude/tools/debug.hxx
.) Non viene definita nella compilazioni di produzione, per definirla usate--enable-dbgutil
.
- La macro
_GLIBCXX_DEBUG
abilita delle asserzioni utili nell'implementazione di libstdc++ STL, ciò influenza la compatibilità ABI; viene abilitata anche da--enable-dbgutil
sulle piattaforme GCC basate su ELF (DA FARE: questa può funzionare su tutte le piattaforme GCC, qualcuno dovrebbe fare dei tentativi).
- La macro
OSL_DEBUG_LEVEL
controlla del codice di debug aggiuntivo potenzialmente eccessivamente dispendioso (ma che non influisce sulla compatibilità). È definito come0
nelle compilazioni complete di produzione, come1
per--enable-debug
/--enable-dbgutil
(abilitando le macro obsoleteOSL_ASSERT
ecc. dainclude/osl/diagnose.h
) e come2
o maggiori con l'argomento esplicitodbglevel=N
permake
.
Debug dei componenti Python di LibreOffice
Per eseguire il debug del caricamento dei componenti in python, modificate la riga DEBUG=0
in pythonloader.py
.
Per eseguire il debug degli script usando il Python Script Provider, impostate la variabile PYSCRIPT_LOG_LEVEL=DEBUG
e (facoltativamente) PYSCRIPT_LOG_STDOUT=0
in modo da ridirigere il file $UserInstallation/Scripts/python/log.txt
.
Per vedere le chiamate al metodo eseguite dal bridge Python/UNO pyuno
, impostate le variabili d'ambiente PYUNO_LOGLEVEL=ARGS
e (facoltativamente) PYUNO_LOGTARGET=mylogfile
.
Se avviate soffice in un terminale, potete usare pdb come debugger a livello di python; per invocarlo e impostare effettivamente un punto di interruzione, aggiungete questa riga in un punto appropriato del vostro codice python, solitamente nella fase di inizializzazione:
import pdb; pdb.set_trace()
Un altra opzione è quella di usare gdb per il debug, che può caricare un file personalizzato per la stampa abbellita che corrisponde alla libreria di python usata da LO. Dopodiché comandi come py-bt
, py-bt-full
, py-print
, py-locals
, py-up
, py-down
e py-list
diventano disponibili dal prompt di gdb, in aggiunta ai comandi di debug propri di gdb per C++. Vedete la documentazione su [1].
Per la versione di python distribuita con LO, il debug con gdb è abilitato da instdir/program/libpython3.5m.so.1.0-gdb.py
; se LO è compilato per una versione di python di sistema, il file potrebbe trovarsi in una posizione non scontata; su Fedora 29 può essere installato con
sudo dnf --enablerepo=fedora-debuginfo --enablerepo=updates-debuginfo install python3-debuginfo
.
Eseguire il debug delle perdite di memoria con valgrind (comprese le perdite di ref-count)
Supponete di sapere che un determinato unit test causa delle perdite di memoria.
Eseguite uno unit test in questo modo:
$ make CppunitTest_cppcanvas_test VALGRIND='memcheck --vgdb=yes --vgdb-error=0 --leak-check=full --suppressions=$$BUILDDIR/solenv/sanitizers/valgrind-suppressions'
poi in un altro terminale eseguite
$ gdb workdir/LinkTarget/Executable/cppunittester
una volta che gdb compare, inserite il comando
(gdb) target remote | vgdb
ora potete eseguire comandi come l'inserimento di punti di interruzione, o continue e verrà controllato il programma eseguito nell'altro terminale
In modo più vantaggioso, potete impostare un punto di interruzione in una posizione in questo modo
(gdb) br cppunittester.cxx:473
che si trova subito dopo che lo unit test è terminato, poi potete impostare un punto di interruzione nel punto del costruttore dell'oggetto di vostro interesse, in questo modo
(gdb) br MyObject::MyObject
quindi prendete nota del valore esadecimale del puntatore "this" nel momento in cui si attiva il punto di interruzione. Poi quando si attiva il punto di interruzione end-of-unit-test, valgrind potrà dirvi chi sta ancora nascondendo un puntatore all'oggetto di vostro interesse con:
(gdb) monitor who_points_at 0xegegegeg
Per un riepilogo di tutti i comandi di debug disponibili in valgrind per il controllo della memoria, vedete Memcheck Monitor Commands.
Debug dei componenti Java di LibreOffice
Se volete eseguire il debug di parti di LibreOffice implementate in Java, GDB non è utile in quanto attualmente non supporta Java in quanto questo viene eseguito in una sua macchina virtuale.
Occasionalmente può essere utile ottenere la stack trace a livello di Java, come quando ci si trova ad un punto morto con del codice Java potenzialmente coinvolto; lo strumento jstack
può stamparlo, semplicemente passategli l'id del processo soffice.bin come argomento.
Per il debug effettivo è più conveniente avere un'IDE (anche se esiste un debugger a riga di comando jdb
). Per avere questo tipo di impostazione, dovete usare il debug remoto di Java ad esempio all'interno di eclipse.
Predisporre LibreOffice all'abilitazione del debug
Per abilitare il debug della Java Virtual Machine, avviate LibreOffice normalmente ed abilitate il debug aggiungendo la versione della jvm che state usando nelle impostazioni degli Strumenti sotto Strumenti > LibreOfficeDev > Avanzate ed aggiungete i seguenti parametri:
-Xdebug
-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n
il primo dei due abilita il debug, il secondo imposta la porta della Virtual Machine, se abilitate il suspend (scrivendo suspend=y invece di suspend=n) la Machine viene fermata fino a che un debugger non le si collega, questo può essere usato per il debug del progresso di avvio della jvm.
Predisporre Eclipse per il debug
In Eclipse, assicuratevi di aver aperto la cartella libreoffice come Progetto Java, quindi potete aggiungere una configurazione di debug del tipo Remote Java Application Questa configurazione di debug dovrà avere:
come tipo di connessione: Standard (socket attach)
e come proprietà della connessione:
Host: localhost
Porta: 8000
Questa porta deve essere la stessa che avete predisposto all'interno di Libreoffice. Se poi avviate il modulo in Java e durante la progressione dell'avvio fate partire la configurazione di debug, dovreste rendervi conto del suo funzionamento quando si ferma ai vostri punti di interruzione.
Debug delle prestazioni (Callgrind)
Callgrind è lo strumento più comunemente usato per cercare problemi delle prestazioni. Vedete i seguenti collegamenti per le istruzioni:
- Come ottenere una trace di Callgrind
- Video sull'uso di Callgrind con KCachegrind per la profilazione.
Visualizzazione dei risultati
Per visualizzare i risultati può essere usato KCachegrind.
- Disattivate il rilevamento dei cicli. Spesso i risultati sono quasi insignificanti per i casi non semplici.
- Disattivate il pulsante 'Relative' ed usate contattori dei cicli assoluti dappertutto, altrimenti è molto semplice perdere il senso delle proporzioni.
- Confrontate tutti i contattori con il totale della barra di stato in fondo per verificarne l'integrità.
Per un modo alternativo di visualizzare i risultati, potete provare gprof2dot.
Debug delle prestazioni (perf)
Il profiler del kernel di linux (perf) produce risultati meno dettagliati di callgrind, ma è molto più conveniente da eseguire (in quanto è un profiler a campione). Con callgrind o perf, non dovete usare una compilazione di debug, ma una compilazione con --enable-symbols
.
Potreste aver bisogno di permessi extra, ad es.
sudo sh -c "echo 0 > /proc/sys/kernel/kptr_restrict"
o anche:
sudo sh -c 'echo 1 >/proc/sys/kernel/perf_event_paranoid'
in modo da usare i simboli del kernel nel risultato.
Usate
perf record -g --pid=`pidof soffice.bin`
per catturare i dati e poi
perf report
per visualizzare il risulato.
Per una visualizzazione molto bella del risultato, installate FlameGraph con il comando:
git clone https://github.com/brendangregg/FlameGraph
e poi eseguendo
perf script | ../FlameGraph/stackcollapse-perf.pl | ../FlameGraph/flamegraph.pl --width 4096 --height 24 > perf.svg
firefox perf.svg
O installate KDAB Hotspot per avere una bella GUI.
Potete esportare un grafico di flamegraph da Hotspot tramite
▸ ▸ . I formati disponibili sono BMP e SVG.Se trovate che i dati risultati siano troppo grezzi, avete due opzioni
- Catturate i dati per un periodo di tempo più lungo, probabilmente eseguendo l'azione più di una volta
- Aumentare la frequenza di campionamento del profiler, ad es.
perf record -g -F 10000 --pid=`pidof soffice.bin`
Se vedete che parti della stack delle chiamate sono mancanti, potreste dover incrementare le dimensioni della stack da catturare con:
perf record --call-graph dwarf,65528 --pid=`pidof soffice.bin`