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

    Note pin.svg

    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`