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 98% complete.
Outdated translations are marked like this.

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

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.

Uso di Voltron

Si veda Development/Voltron.

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:

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 è possibile utilizzare KDAB Hotspot.

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`