Jump to content

Sviluppo - Come eseguire il debug

From The Document Foundation Wiki
This page is a translated version of the page Development/How to debug and the translation is 100% complete.

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 Strumenti ▸ Opzioni ▸ LibreOffice ▸ Avanzate; è 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 (File ▸ 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 è

  1. aggiungere un punto di interruzione all'inizio del blocco di try, in questo caso la riga 608
  2. una volta che si interrompe in quel punto, rimuovere il punto di interruzione e aggiungerne uno per ogni eccezione di C++
  3. 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 Debug ▸ Attach to Process.... L'esecuzione si fermerà quanto il punto di interruzione sarà raggiunto (per fermare l'esecuzione immediatamente, usate Debug ▸ Break All). Durante il debug Visual Studio apre automaticamente i file sorgenti del codice eseguito. L'elenco delle chiamate è disponibile nella finestra di nome Call stack.

Uso di Time Travel per il debug con windbg

Vedere https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/time-travel-debugging-overview. È possibile usare questa funzione per catturare una sola volta lo scenario del malfunzionamento e riprodurre le sue parti quante volte necessario nel debugger.

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: Tools ▸ Options ▸ Debugging ▸ Symbols 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 ignorare java.exe eseguendo drconfig.exe -quiet -reg java.exe -norun. (Stranamente, il processo interno jvm.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

Nota:
Per vedere la lista completa delle variabili d'ambiente usate in LibreOffice, vedete Development/Environment variables.

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 OpenGL
  • SAL_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à standard assert. È 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 e SAL_LOG_WARN controllano se le rispettive funzionalità SAL_INFO e SAL_WARN, da include/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'ambiente SAL_LOG (vedete la documentazione in include/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 obsolete DBG_ASSERT ecc. da include/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 come 0 nelle compilazioni complete di produzione, come 1 per --enable-debug/--enable-dbgutil (abilitando le macro obsolete OSL_ASSERT ecc. da include/osl/diagnose.h) e come 2 o maggiori con l'argomento esplicito dbglevel=N per make.

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:

  1. Come ottenere una trace di Callgrind
  2. Video sull'uso di Callgrind con KCachegrind per la profilazione.

Visualizzazione dei risultati

Per visualizzare i risultati può essere usato KCachegrind.

  1. Disattivate il rilevamento dei cicli. Spesso i risultati sono quasi insignificanti per i casi non semplici.
  2. Disattivate il pulsante 'Relative' ed usate contattori dei cicli assoluti dappertutto, altrimenti è molto semplice perdere il senso delle proporzioni.
  3. 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 View ▸ Export ▸ Flamegraph. 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`