Portál AbcLinuxu, 30. dubna 2025 16:48

Pár tipů k callgrindu, debug symbolům, stack trace (C++)

7.8.2009 01:33 | Přečteno: 1373× | programování | Výběrový blog

Tipy pro přesnější použití valgrind/callgrind, manipulaci s debugovacími symboly a zjištění stack trace "zaživa".

Callgrind - měření části kódu

Nejjednodušší použití callgrindu je zavolat ho na celou binárku. Důležité je mít binárku přeloženu s debugovacími symboly (je možné kombinovat přepínače -Ox a -g u gcc). Budu používat cpuspinner.cpp jako příklad. Měření celého běhu programu:

valgrind --tool=callgrind ./cpuspinner

Když potřebujete změřit jenom část, zaincludujte valgrind/callgrind.h a můžete použít makra:

Callgrind pak spustíme s parametrem --instr-atstart=no, aby neměřil dokud nenarazí na první z maker (viz příklad v cpuspinner.cpp):

valgrind --tool=callgrind --instr-atstart=no ./cpuspinner

V callgrind.h je vícero maker, kterými můžete ovládat měření a generování dat (lze "dumpnout" několik měření v jednom běhu apod.), viz manuál callgrindu. Makra z callgrind.h nědělají nic pokud program není spuštěn pod callgrindem.

Callgrind - interaktivní ovládání

K callgrindu je přibalen nástroj callgrind_control, kterým lze vypnout/zapnout instrumentaci k probíhajícímu měření. Hodí se to třeba k interaktivním GUI aplikacím. Spustíte valgrind s parametrem --instr-atstart=no, v momentě kdy chcete začít měřit, zavoláte

callgrind_control -i on

a pro vypnutí měření

callgrind_control -i off

"Přesýpání" debugovacích symbolů

Když použijete přepínač -g u gcc, informace o debugovacích symbolech se nakonec dostanou do výsledné binárky (executable, shared object). Typicky se binárky před "releasem" strip-nou, čímž se debugovací informace zahodí.

Jenže může se vyskytnout bug, který shodí aplikaci a není (jednoduše) replikovatelný. Můžete získat coredump, ten ale není bez debug symbolů přílíš užitečný. Je ovšem způsom jak zachovat debugovací symboly odděleně od binárek. Příklad s cpuspinner:

objcopy --only-keep-debug cpuspinner cpuspinner.debug
strip cpuspinner
objcopy --add-gnu-debuglink=cpuspinner.debug cpuspinner

Tyto tři příkazy nejprve vygenerují kopii debugovacích symbolů do souboru cpuspinner.debug. Pak se stripnou z původního cpuspinner a nakonec se do cpuspinner uloží odkaz, že debugovací symboly existují v souboru cpuspinner.debug. Gdb, jeho nadstavby a valgrind pak umí debug symboly najít v takhle vygenerovaných separátních debug souborech. (Pozn.: snad jednou se mi stalo, že je nějaký starší valgrind "neviděl"). Debugovací symboly lze někam odložit, kdyby se něco podobného muselo řešit. Velmi dobře se komprimují s LZMA kompresí.

Zjištění stack trace za běhu

Někdy může být užitečné zjistit stack trace za běhu, např. při logování výjimky, jak to umí Java nebo Python. V C++ to za jistých podmínek jde. Tady je jeden článek generování stack trace v C++, přímý odkaz na zdrojáky dbg::stack.

Poznámka: aby dbg::stack fungoval, musíte svůj program přeložit s -rdynamic a nesmíte použít flag -fomit-frame-pointer. Funguje to i na x86_64, jenom je potřeba doplnit jeden ifdef do stack.cpp.

       

Hodnocení: 100 %

        špatnédobré        

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

Komentáře

Nástroje: Začni sledovat (0) ?Zašle upozornění na váš email při vložení nového komentáře. , Tisk

Vložit další komentář

17.8.2009 11:16 Ivan
Rozbalit Rozbalit vše Dik za tip.
Odpovědět | Sbalit | Link | Blokovat | Admin

Ahoj diky za tip. Ten dbg::stack vypada dobre. Taky jsem se o neco takovyho pokousel:

Marka __HERE__ a __HERE__SHORT__ slouzi k identifikaci mista ve zdrojacich. Pouzivam to pri vyhazovani vyjimek. Mam vlastni typ pro vyjimku, ktera ma 1.  argument konstruktoru std::string.

A  pouzivam to takhle:

throw(OciException(__HERE__, "Not implemented yet\n"));

--- snap ---

#ifdef __GNUC__
        #define __HERE_SHORT__ ::std::string(((strrchr(__FILE__, '/') ?: __FILE__ - 1) + 1)) + ":"__HERE1__(__LINE__)
        #define __HERE__ ::trotl::str_backtrace() + __HERE3__(__LINE__, __FILE__)
        #define __HERE1__(x)   STR(x)"\t" + __PRETTY_FUNCTION__
        #define __HERE2__(x,y) ::std::string("("y":" STR(x)"(") +  __PRETTY_FUNCTION__ +")"
        #define __HERE3__(x,y) ::std::string("\n(") + __PRETTY_FUNCTION__ + ") " y ":" STR(x) + "\n"
        #define STR(a) #a
#else

...

inline ::std::string str_backtrace()
{
         ::std::stringstream ret;
#ifdef __GNUC__
        void *buffer[TROTL_BACKTRACE_DEPTH];

        int bsize = ::backtrace(buffer, TROTL_BACKTRACE_DEPTH);
        char **names = backtrace_symbols (buffer, bsize);
        for(int i=0; i<bsize; i++)
                ret << names[i] << std::endl;
        free(names);
#endif
        return ret.str();
}

--- snap ---

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.