12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506 |
- /* Branch trace support for GDB, the GNU debugger.
- Copyright (C) 2013-2022 Free Software Foundation, Inc.
- Contributed by Intel Corp. <markus.t.metzger@intel.com>
- This file is part of GDB.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
- #include "defs.h"
- #include "btrace.h"
- #include "gdbthread.h"
- #include "inferior.h"
- #include "target.h"
- #include "record.h"
- #include "symtab.h"
- #include "disasm.h"
- #include "source.h"
- #include "filenames.h"
- #include "xml-support.h"
- #include "regcache.h"
- #include "gdbsupport/rsp-low.h"
- #include "gdbcmd.h"
- #include "cli/cli-utils.h"
- #include "gdbarch.h"
- /* For maintenance commands. */
- #include "record-btrace.h"
- #include <inttypes.h>
- #include <ctype.h>
- #include <algorithm>
- /* Command lists for btrace maintenance commands. */
- static struct cmd_list_element *maint_btrace_cmdlist;
- static struct cmd_list_element *maint_btrace_set_cmdlist;
- static struct cmd_list_element *maint_btrace_show_cmdlist;
- static struct cmd_list_element *maint_btrace_pt_set_cmdlist;
- static struct cmd_list_element *maint_btrace_pt_show_cmdlist;
- /* Control whether to skip PAD packets when computing the packet history. */
- static bool maint_btrace_pt_skip_pad = true;
- static void btrace_add_pc (struct thread_info *tp);
- /* Print a record debug message. Use do ... while (0) to avoid ambiguities
- when used in if statements. */
- #define DEBUG(msg, args...) \
- do \
- { \
- if (record_debug != 0) \
- gdb_printf (gdb_stdlog, \
- "[btrace] " msg "\n", ##args); \
- } \
- while (0)
- #define DEBUG_FTRACE(msg, args...) DEBUG ("[ftrace] " msg, ##args)
- /* Return the function name of a recorded function segment for printing.
- This function never returns NULL. */
- static const char *
- ftrace_print_function_name (const struct btrace_function *bfun)
- {
- struct minimal_symbol *msym;
- struct symbol *sym;
- msym = bfun->msym;
- sym = bfun->sym;
- if (sym != NULL)
- return sym->print_name ();
- if (msym != NULL)
- return msym->print_name ();
- return "<unknown>";
- }
- /* Return the file name of a recorded function segment for printing.
- This function never returns NULL. */
- static const char *
- ftrace_print_filename (const struct btrace_function *bfun)
- {
- struct symbol *sym;
- const char *filename;
- sym = bfun->sym;
- if (sym != NULL)
- filename = symtab_to_filename_for_display (symbol_symtab (sym));
- else
- filename = "<unknown>";
- return filename;
- }
- /* Return a string representation of the address of an instruction.
- This function never returns NULL. */
- static const char *
- ftrace_print_insn_addr (const struct btrace_insn *insn)
- {
- if (insn == NULL)
- return "<nil>";
- return core_addr_to_string_nz (insn->pc);
- }
- /* Print an ftrace debug status message. */
- static void
- ftrace_debug (const struct btrace_function *bfun, const char *prefix)
- {
- const char *fun, *file;
- unsigned int ibegin, iend;
- int level;
- fun = ftrace_print_function_name (bfun);
- file = ftrace_print_filename (bfun);
- level = bfun->level;
- ibegin = bfun->insn_offset;
- iend = ibegin + bfun->insn.size ();
- DEBUG_FTRACE ("%s: fun = %s, file = %s, level = %d, insn = [%u; %u)",
- prefix, fun, file, level, ibegin, iend);
- }
- /* Return the number of instructions in a given function call segment. */
- static unsigned int
- ftrace_call_num_insn (const struct btrace_function* bfun)
- {
- if (bfun == NULL)
- return 0;
- /* A gap is always counted as one instruction. */
- if (bfun->errcode != 0)
- return 1;
- return bfun->insn.size ();
- }
- /* Return the function segment with the given NUMBER or NULL if no such segment
- exists. BTINFO is the branch trace information for the current thread. */
- static struct btrace_function *
- ftrace_find_call_by_number (struct btrace_thread_info *btinfo,
- unsigned int number)
- {
- if (number == 0 || number > btinfo->functions.size ())
- return NULL;
- return &btinfo->functions[number - 1];
- }
- /* A const version of the function above. */
- static const struct btrace_function *
- ftrace_find_call_by_number (const struct btrace_thread_info *btinfo,
- unsigned int number)
- {
- if (number == 0 || number > btinfo->functions.size ())
- return NULL;
- return &btinfo->functions[number - 1];
- }
- /* Return non-zero if BFUN does not match MFUN and FUN,
- return zero otherwise. */
- static int
- ftrace_function_switched (const struct btrace_function *bfun,
- const struct minimal_symbol *mfun,
- const struct symbol *fun)
- {
- struct minimal_symbol *msym;
- struct symbol *sym;
- msym = bfun->msym;
- sym = bfun->sym;
- /* If the minimal symbol changed, we certainly switched functions. */
- if (mfun != NULL && msym != NULL
- && strcmp (mfun->linkage_name (), msym->linkage_name ()) != 0)
- return 1;
- /* If the symbol changed, we certainly switched functions. */
- if (fun != NULL && sym != NULL)
- {
- const char *bfname, *fname;
- /* Check the function name. */
- if (strcmp (fun->linkage_name (), sym->linkage_name ()) != 0)
- return 1;
- /* Check the location of those functions, as well. */
- bfname = symtab_to_fullname (symbol_symtab (sym));
- fname = symtab_to_fullname (symbol_symtab (fun));
- if (filename_cmp (fname, bfname) != 0)
- return 1;
- }
- /* If we lost symbol information, we switched functions. */
- if (!(msym == NULL && sym == NULL) && mfun == NULL && fun == NULL)
- return 1;
- /* If we gained symbol information, we switched functions. */
- if (msym == NULL && sym == NULL && !(mfun == NULL && fun == NULL))
- return 1;
- return 0;
- }
- /* Allocate and initialize a new branch trace function segment at the end of
- the trace.
- BTINFO is the branch trace information for the current thread.
- MFUN and FUN are the symbol information we have for this function.
- This invalidates all struct btrace_function pointer currently held. */
- static struct btrace_function *
- ftrace_new_function (struct btrace_thread_info *btinfo,
- struct minimal_symbol *mfun,
- struct symbol *fun)
- {
- int level;
- unsigned int number, insn_offset;
- if (btinfo->functions.empty ())
- {
- /* Start counting NUMBER and INSN_OFFSET at one. */
- level = 0;
- number = 1;
- insn_offset = 1;
- }
- else
- {
- const struct btrace_function *prev = &btinfo->functions.back ();
- level = prev->level;
- number = prev->number + 1;
- insn_offset = prev->insn_offset + ftrace_call_num_insn (prev);
- }
- btinfo->functions.emplace_back (mfun, fun, number, insn_offset, level);
- return &btinfo->functions.back ();
- }
- /* Update the UP field of a function segment. */
- static void
- ftrace_update_caller (struct btrace_function *bfun,
- struct btrace_function *caller,
- btrace_function_flags flags)
- {
- if (bfun->up != 0)
- ftrace_debug (bfun, "updating caller");
- bfun->up = caller->number;
- bfun->flags = flags;
- ftrace_debug (bfun, "set caller");
- ftrace_debug (caller, "..to");
- }
- /* Fix up the caller for all segments of a function. */
- static void
- ftrace_fixup_caller (struct btrace_thread_info *btinfo,
- struct btrace_function *bfun,
- struct btrace_function *caller,
- btrace_function_flags flags)
- {
- unsigned int prev, next;
- prev = bfun->prev;
- next = bfun->next;
- ftrace_update_caller (bfun, caller, flags);
- /* Update all function segments belonging to the same function. */
- for (; prev != 0; prev = bfun->prev)
- {
- bfun = ftrace_find_call_by_number (btinfo, prev);
- ftrace_update_caller (bfun, caller, flags);
- }
- for (; next != 0; next = bfun->next)
- {
- bfun = ftrace_find_call_by_number (btinfo, next);
- ftrace_update_caller (bfun, caller, flags);
- }
- }
- /* Add a new function segment for a call at the end of the trace.
- BTINFO is the branch trace information for the current thread.
- MFUN and FUN are the symbol information we have for this function. */
- static struct btrace_function *
- ftrace_new_call (struct btrace_thread_info *btinfo,
- struct minimal_symbol *mfun,
- struct symbol *fun)
- {
- const unsigned int length = btinfo->functions.size ();
- struct btrace_function *bfun = ftrace_new_function (btinfo, mfun, fun);
- bfun->up = length;
- bfun->level += 1;
- ftrace_debug (bfun, "new call");
- return bfun;
- }
- /* Add a new function segment for a tail call at the end of the trace.
- BTINFO is the branch trace information for the current thread.
- MFUN and FUN are the symbol information we have for this function. */
- static struct btrace_function *
- ftrace_new_tailcall (struct btrace_thread_info *btinfo,
- struct minimal_symbol *mfun,
- struct symbol *fun)
- {
- const unsigned int length = btinfo->functions.size ();
- struct btrace_function *bfun = ftrace_new_function (btinfo, mfun, fun);
- bfun->up = length;
- bfun->level += 1;
- bfun->flags |= BFUN_UP_LINKS_TO_TAILCALL;
- ftrace_debug (bfun, "new tail call");
- return bfun;
- }
- /* Return the caller of BFUN or NULL if there is none. This function skips
- tail calls in the call chain. BTINFO is the branch trace information for
- the current thread. */
- static struct btrace_function *
- ftrace_get_caller (struct btrace_thread_info *btinfo,
- struct btrace_function *bfun)
- {
- for (; bfun != NULL; bfun = ftrace_find_call_by_number (btinfo, bfun->up))
- if ((bfun->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
- return ftrace_find_call_by_number (btinfo, bfun->up);
- return NULL;
- }
- /* Find the innermost caller in the back trace of BFUN with MFUN/FUN
- symbol information. BTINFO is the branch trace information for the current
- thread. */
- static struct btrace_function *
- ftrace_find_caller (struct btrace_thread_info *btinfo,
- struct btrace_function *bfun,
- struct minimal_symbol *mfun,
- struct symbol *fun)
- {
- for (; bfun != NULL; bfun = ftrace_find_call_by_number (btinfo, bfun->up))
- {
- /* Skip functions with incompatible symbol information. */
- if (ftrace_function_switched (bfun, mfun, fun))
- continue;
- /* This is the function segment we're looking for. */
- break;
- }
- return bfun;
- }
- /* Find the innermost caller in the back trace of BFUN, skipping all
- function segments that do not end with a call instruction (e.g.
- tail calls ending with a jump). BTINFO is the branch trace information for
- the current thread. */
- static struct btrace_function *
- ftrace_find_call (struct btrace_thread_info *btinfo,
- struct btrace_function *bfun)
- {
- for (; bfun != NULL; bfun = ftrace_find_call_by_number (btinfo, bfun->up))
- {
- /* Skip gaps. */
- if (bfun->errcode != 0)
- continue;
- btrace_insn &last = bfun->insn.back ();
- if (last.iclass == BTRACE_INSN_CALL)
- break;
- }
- return bfun;
- }
- /* Add a continuation segment for a function into which we return at the end of
- the trace.
- BTINFO is the branch trace information for the current thread.
- MFUN and FUN are the symbol information we have for this function. */
- static struct btrace_function *
- ftrace_new_return (struct btrace_thread_info *btinfo,
- struct minimal_symbol *mfun,
- struct symbol *fun)
- {
- struct btrace_function *prev, *bfun, *caller;
- bfun = ftrace_new_function (btinfo, mfun, fun);
- prev = ftrace_find_call_by_number (btinfo, bfun->number - 1);
- /* It is important to start at PREV's caller. Otherwise, we might find
- PREV itself, if PREV is a recursive function. */
- caller = ftrace_find_call_by_number (btinfo, prev->up);
- caller = ftrace_find_caller (btinfo, caller, mfun, fun);
- if (caller != NULL)
- {
- /* The caller of PREV is the preceding btrace function segment in this
- function instance. */
- gdb_assert (caller->next == 0);
- caller->next = bfun->number;
- bfun->prev = caller->number;
- /* Maintain the function level. */
- bfun->level = caller->level;
- /* Maintain the call stack. */
- bfun->up = caller->up;
- bfun->flags = caller->flags;
- ftrace_debug (bfun, "new return");
- }
- else
- {
- /* We did not find a caller. This could mean that something went
- wrong or that the call is simply not included in the trace. */
- /* Let's search for some actual call. */
- caller = ftrace_find_call_by_number (btinfo, prev->up);
- caller = ftrace_find_call (btinfo, caller);
- if (caller == NULL)
- {
- /* There is no call in PREV's back trace. We assume that the
- branch trace did not include it. */
- /* Let's find the topmost function and add a new caller for it.
- This should handle a series of initial tail calls. */
- while (prev->up != 0)
- prev = ftrace_find_call_by_number (btinfo, prev->up);
- bfun->level = prev->level - 1;
- /* Fix up the call stack for PREV. */
- ftrace_fixup_caller (btinfo, prev, bfun, BFUN_UP_LINKS_TO_RET);
- ftrace_debug (bfun, "new return - no caller");
- }
- else
- {
- /* There is a call in PREV's back trace to which we should have
- returned but didn't. Let's start a new, separate back trace
- from PREV's level. */
- bfun->level = prev->level - 1;
- /* We fix up the back trace for PREV but leave other function segments
- on the same level as they are.
- This should handle things like schedule () correctly where we're
- switching contexts. */
- prev->up = bfun->number;
- prev->flags = BFUN_UP_LINKS_TO_RET;
- ftrace_debug (bfun, "new return - unknown caller");
- }
- }
- return bfun;
- }
- /* Add a new function segment for a function switch at the end of the trace.
- BTINFO is the branch trace information for the current thread.
- MFUN and FUN are the symbol information we have for this function. */
- static struct btrace_function *
- ftrace_new_switch (struct btrace_thread_info *btinfo,
- struct minimal_symbol *mfun,
- struct symbol *fun)
- {
- struct btrace_function *prev, *bfun;
- /* This is an unexplained function switch. We can't really be sure about the
- call stack, yet the best I can think of right now is to preserve it. */
- bfun = ftrace_new_function (btinfo, mfun, fun);
- prev = ftrace_find_call_by_number (btinfo, bfun->number - 1);
- bfun->up = prev->up;
- bfun->flags = prev->flags;
- ftrace_debug (bfun, "new switch");
- return bfun;
- }
- /* Add a new function segment for a gap in the trace due to a decode error at
- the end of the trace.
- BTINFO is the branch trace information for the current thread.
- ERRCODE is the format-specific error code. */
- static struct btrace_function *
- ftrace_new_gap (struct btrace_thread_info *btinfo, int errcode,
- std::vector<unsigned int> &gaps)
- {
- struct btrace_function *bfun;
- if (btinfo->functions.empty ())
- bfun = ftrace_new_function (btinfo, NULL, NULL);
- else
- {
- /* We hijack the previous function segment if it was empty. */
- bfun = &btinfo->functions.back ();
- if (bfun->errcode != 0 || !bfun->insn.empty ())
- bfun = ftrace_new_function (btinfo, NULL, NULL);
- }
- bfun->errcode = errcode;
- gaps.push_back (bfun->number);
- ftrace_debug (bfun, "new gap");
- return bfun;
- }
- /* Update the current function segment at the end of the trace in BTINFO with
- respect to the instruction at PC. This may create new function segments.
- Return the chronologically latest function segment, never NULL. */
- static struct btrace_function *
- ftrace_update_function (struct btrace_thread_info *btinfo, CORE_ADDR pc)
- {
- struct bound_minimal_symbol bmfun;
- struct minimal_symbol *mfun;
- struct symbol *fun;
- struct btrace_function *bfun;
- /* Try to determine the function we're in. We use both types of symbols
- to avoid surprises when we sometimes get a full symbol and sometimes
- only a minimal symbol. */
- fun = find_pc_function (pc);
- bmfun = lookup_minimal_symbol_by_pc (pc);
- mfun = bmfun.minsym;
- if (fun == NULL && mfun == NULL)
- DEBUG_FTRACE ("no symbol at %s", core_addr_to_string_nz (pc));
- /* If we didn't have a function, we create one. */
- if (btinfo->functions.empty ())
- return ftrace_new_function (btinfo, mfun, fun);
- /* If we had a gap before, we create a function. */
- bfun = &btinfo->functions.back ();
- if (bfun->errcode != 0)
- return ftrace_new_function (btinfo, mfun, fun);
- /* Check the last instruction, if we have one.
- We do this check first, since it allows us to fill in the call stack
- links in addition to the normal flow links. */
- btrace_insn *last = NULL;
- if (!bfun->insn.empty ())
- last = &bfun->insn.back ();
- if (last != NULL)
- {
- switch (last->iclass)
- {
- case BTRACE_INSN_RETURN:
- {
- const char *fname;
- /* On some systems, _dl_runtime_resolve returns to the resolved
- function instead of jumping to it. From our perspective,
- however, this is a tailcall.
- If we treated it as return, we wouldn't be able to find the
- resolved function in our stack back trace. Hence, we would
- lose the current stack back trace and start anew with an empty
- back trace. When the resolved function returns, we would then
- create a stack back trace with the same function names but
- different frame id's. This will confuse stepping. */
- fname = ftrace_print_function_name (bfun);
- if (strcmp (fname, "_dl_runtime_resolve") == 0)
- return ftrace_new_tailcall (btinfo, mfun, fun);
- return ftrace_new_return (btinfo, mfun, fun);
- }
- case BTRACE_INSN_CALL:
- /* Ignore calls to the next instruction. They are used for PIC. */
- if (last->pc + last->size == pc)
- break;
- return ftrace_new_call (btinfo, mfun, fun);
- case BTRACE_INSN_JUMP:
- {
- CORE_ADDR start;
- start = get_pc_function_start (pc);
- /* A jump to the start of a function is (typically) a tail call. */
- if (start == pc)
- return ftrace_new_tailcall (btinfo, mfun, fun);
- /* Some versions of _Unwind_RaiseException use an indirect
- jump to 'return' to the exception handler of the caller
- handling the exception instead of a return. Let's restrict
- this heuristic to that and related functions. */
- const char *fname = ftrace_print_function_name (bfun);
- if (strncmp (fname, "_Unwind_", strlen ("_Unwind_")) == 0)
- {
- struct btrace_function *caller
- = ftrace_find_call_by_number (btinfo, bfun->up);
- caller = ftrace_find_caller (btinfo, caller, mfun, fun);
- if (caller != NULL)
- return ftrace_new_return (btinfo, mfun, fun);
- }
- /* If we can't determine the function for PC, we treat a jump at
- the end of the block as tail call if we're switching functions
- and as an intra-function branch if we don't. */
- if (start == 0 && ftrace_function_switched (bfun, mfun, fun))
- return ftrace_new_tailcall (btinfo, mfun, fun);
- break;
- }
- }
- }
- /* Check if we're switching functions for some other reason. */
- if (ftrace_function_switched (bfun, mfun, fun))
- {
- DEBUG_FTRACE ("switching from %s in %s at %s",
- ftrace_print_insn_addr (last),
- ftrace_print_function_name (bfun),
- ftrace_print_filename (bfun));
- return ftrace_new_switch (btinfo, mfun, fun);
- }
- return bfun;
- }
- /* Add the instruction at PC to BFUN's instructions. */
- static void
- ftrace_update_insns (struct btrace_function *bfun, const btrace_insn &insn)
- {
- bfun->insn.push_back (insn);
- if (record_debug > 1)
- ftrace_debug (bfun, "update insn");
- }
- /* Classify the instruction at PC. */
- static enum btrace_insn_class
- ftrace_classify_insn (struct gdbarch *gdbarch, CORE_ADDR pc)
- {
- enum btrace_insn_class iclass;
- iclass = BTRACE_INSN_OTHER;
- try
- {
- if (gdbarch_insn_is_call (gdbarch, pc))
- iclass = BTRACE_INSN_CALL;
- else if (gdbarch_insn_is_ret (gdbarch, pc))
- iclass = BTRACE_INSN_RETURN;
- else if (gdbarch_insn_is_jump (gdbarch, pc))
- iclass = BTRACE_INSN_JUMP;
- }
- catch (const gdb_exception_error &error)
- {
- }
- return iclass;
- }
- /* Try to match the back trace at LHS to the back trace at RHS. Returns the
- number of matching function segments or zero if the back traces do not
- match. BTINFO is the branch trace information for the current thread. */
- static int
- ftrace_match_backtrace (struct btrace_thread_info *btinfo,
- struct btrace_function *lhs,
- struct btrace_function *rhs)
- {
- int matches;
- for (matches = 0; lhs != NULL && rhs != NULL; ++matches)
- {
- if (ftrace_function_switched (lhs, rhs->msym, rhs->sym))
- return 0;
- lhs = ftrace_get_caller (btinfo, lhs);
- rhs = ftrace_get_caller (btinfo, rhs);
- }
- return matches;
- }
- /* Add ADJUSTMENT to the level of BFUN and succeeding function segments.
- BTINFO is the branch trace information for the current thread. */
- static void
- ftrace_fixup_level (struct btrace_thread_info *btinfo,
- struct btrace_function *bfun, int adjustment)
- {
- if (adjustment == 0)
- return;
- DEBUG_FTRACE ("fixup level (%+d)", adjustment);
- ftrace_debug (bfun, "..bfun");
- while (bfun != NULL)
- {
- bfun->level += adjustment;
- bfun = ftrace_find_call_by_number (btinfo, bfun->number + 1);
- }
- }
- /* Recompute the global level offset. Traverse the function trace and compute
- the global level offset as the negative of the minimal function level. */
- static void
- ftrace_compute_global_level_offset (struct btrace_thread_info *btinfo)
- {
- int level = INT_MAX;
- if (btinfo == NULL)
- return;
- if (btinfo->functions.empty ())
- return;
- unsigned int length = btinfo->functions.size() - 1;
- for (unsigned int i = 0; i < length; ++i)
- level = std::min (level, btinfo->functions[i].level);
- /* The last function segment contains the current instruction, which is not
- really part of the trace. If it contains just this one instruction, we
- ignore the segment. */
- struct btrace_function *last = &btinfo->functions.back();
- if (last->insn.size () != 1)
- level = std::min (level, last->level);
- DEBUG_FTRACE ("setting global level offset: %d", -level);
- btinfo->level = -level;
- }
- /* Connect the function segments PREV and NEXT in a bottom-to-top walk as in
- ftrace_connect_backtrace. BTINFO is the branch trace information for the
- current thread. */
- static void
- ftrace_connect_bfun (struct btrace_thread_info *btinfo,
- struct btrace_function *prev,
- struct btrace_function *next)
- {
- DEBUG_FTRACE ("connecting...");
- ftrace_debug (prev, "..prev");
- ftrace_debug (next, "..next");
- /* The function segments are not yet connected. */
- gdb_assert (prev->next == 0);
- gdb_assert (next->prev == 0);
- prev->next = next->number;
- next->prev = prev->number;
- /* We may have moved NEXT to a different function level. */
- ftrace_fixup_level (btinfo, next, prev->level - next->level);
- /* If we run out of back trace for one, let's use the other's. */
- if (prev->up == 0)
- {
- const btrace_function_flags flags = next->flags;
- next = ftrace_find_call_by_number (btinfo, next->up);
- if (next != NULL)
- {
- DEBUG_FTRACE ("using next's callers");
- ftrace_fixup_caller (btinfo, prev, next, flags);
- }
- }
- else if (next->up == 0)
- {
- const btrace_function_flags flags = prev->flags;
- prev = ftrace_find_call_by_number (btinfo, prev->up);
- if (prev != NULL)
- {
- DEBUG_FTRACE ("using prev's callers");
- ftrace_fixup_caller (btinfo, next, prev, flags);
- }
- }
- else
- {
- /* PREV may have a tailcall caller, NEXT can't. If it does, fixup the up
- link to add the tail callers to NEXT's back trace.
- This removes NEXT->UP from NEXT's back trace. It will be added back
- when connecting NEXT and PREV's callers - provided they exist.
- If PREV's back trace consists of a series of tail calls without an
- actual call, there will be no further connection and NEXT's caller will
- be removed for good. To catch this case, we handle it here and connect
- the top of PREV's back trace to NEXT's caller. */
- if ((prev->flags & BFUN_UP_LINKS_TO_TAILCALL) != 0)
- {
- struct btrace_function *caller;
- btrace_function_flags next_flags, prev_flags;
- /* We checked NEXT->UP above so CALLER can't be NULL. */
- caller = ftrace_find_call_by_number (btinfo, next->up);
- next_flags = next->flags;
- prev_flags = prev->flags;
- DEBUG_FTRACE ("adding prev's tail calls to next");
- prev = ftrace_find_call_by_number (btinfo, prev->up);
- ftrace_fixup_caller (btinfo, next, prev, prev_flags);
- for (; prev != NULL; prev = ftrace_find_call_by_number (btinfo,
- prev->up))
- {
- /* At the end of PREV's back trace, continue with CALLER. */
- if (prev->up == 0)
- {
- DEBUG_FTRACE ("fixing up link for tailcall chain");
- ftrace_debug (prev, "..top");
- ftrace_debug (caller, "..up");
- ftrace_fixup_caller (btinfo, prev, caller, next_flags);
- /* If we skipped any tail calls, this may move CALLER to a
- different function level.
- Note that changing CALLER's level is only OK because we
- know that this is the last iteration of the bottom-to-top
- walk in ftrace_connect_backtrace.
- Otherwise we will fix up CALLER's level when we connect it
- to PREV's caller in the next iteration. */
- ftrace_fixup_level (btinfo, caller,
- prev->level - caller->level - 1);
- break;
- }
- /* There's nothing to do if we find a real call. */
- if ((prev->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
- {
- DEBUG_FTRACE ("will fix up link in next iteration");
- break;
- }
- }
- }
- }
- }
- /* Connect function segments on the same level in the back trace at LHS and RHS.
- The back traces at LHS and RHS are expected to match according to
- ftrace_match_backtrace. BTINFO is the branch trace information for the
- current thread. */
- static void
- ftrace_connect_backtrace (struct btrace_thread_info *btinfo,
- struct btrace_function *lhs,
- struct btrace_function *rhs)
- {
- while (lhs != NULL && rhs != NULL)
- {
- struct btrace_function *prev, *next;
- gdb_assert (!ftrace_function_switched (lhs, rhs->msym, rhs->sym));
- /* Connecting LHS and RHS may change the up link. */
- prev = lhs;
- next = rhs;
- lhs = ftrace_get_caller (btinfo, lhs);
- rhs = ftrace_get_caller (btinfo, rhs);
- ftrace_connect_bfun (btinfo, prev, next);
- }
- }
- /* Bridge the gap between two function segments left and right of a gap if their
- respective back traces match in at least MIN_MATCHES functions. BTINFO is
- the branch trace information for the current thread.
- Returns non-zero if the gap could be bridged, zero otherwise. */
- static int
- ftrace_bridge_gap (struct btrace_thread_info *btinfo,
- struct btrace_function *lhs, struct btrace_function *rhs,
- int min_matches)
- {
- struct btrace_function *best_l, *best_r, *cand_l, *cand_r;
- int best_matches;
- DEBUG_FTRACE ("checking gap at insn %u (req matches: %d)",
- rhs->insn_offset - 1, min_matches);
- best_matches = 0;
- best_l = NULL;
- best_r = NULL;
- /* We search the back traces of LHS and RHS for valid connections and connect
- the two function segments that give the longest combined back trace. */
- for (cand_l = lhs; cand_l != NULL;
- cand_l = ftrace_get_caller (btinfo, cand_l))
- for (cand_r = rhs; cand_r != NULL;
- cand_r = ftrace_get_caller (btinfo, cand_r))
- {
- int matches;
- matches = ftrace_match_backtrace (btinfo, cand_l, cand_r);
- if (best_matches < matches)
- {
- best_matches = matches;
- best_l = cand_l;
- best_r = cand_r;
- }
- }
- /* We need at least MIN_MATCHES matches. */
- gdb_assert (min_matches > 0);
- if (best_matches < min_matches)
- return 0;
- DEBUG_FTRACE ("..matches: %d", best_matches);
- /* We will fix up the level of BEST_R and succeeding function segments such
- that BEST_R's level matches BEST_L's when we connect BEST_L to BEST_R.
- This will ignore the level of RHS and following if BEST_R != RHS. I.e. if
- BEST_R is a successor of RHS in the back trace of RHS (phases 1 and 3).
- To catch this, we already fix up the level here where we can start at RHS
- instead of at BEST_R. We will ignore the level fixup when connecting
- BEST_L to BEST_R as they will already be on the same level. */
- ftrace_fixup_level (btinfo, rhs, best_l->level - best_r->level);
- ftrace_connect_backtrace (btinfo, best_l, best_r);
- return best_matches;
- }
- /* Try to bridge gaps due to overflow or decode errors by connecting the
- function segments that are separated by the gap. */
- static void
- btrace_bridge_gaps (struct thread_info *tp, std::vector<unsigned int> &gaps)
- {
- struct btrace_thread_info *btinfo = &tp->btrace;
- std::vector<unsigned int> remaining;
- int min_matches;
- DEBUG ("bridge gaps");
- /* We require a minimum amount of matches for bridging a gap. The number of
- required matches will be lowered with each iteration.
- The more matches the higher our confidence that the bridging is correct.
- For big gaps or small traces, however, it may not be feasible to require a
- high number of matches. */
- for (min_matches = 5; min_matches > 0; --min_matches)
- {
- /* Let's try to bridge as many gaps as we can. In some cases, we need to
- skip a gap and revisit it again after we closed later gaps. */
- while (!gaps.empty ())
- {
- for (const unsigned int number : gaps)
- {
- struct btrace_function *gap, *lhs, *rhs;
- int bridged;
- gap = ftrace_find_call_by_number (btinfo, number);
- /* We may have a sequence of gaps if we run from one error into
- the next as we try to re-sync onto the trace stream. Ignore
- all but the leftmost gap in such a sequence.
- Also ignore gaps at the beginning of the trace. */
- lhs = ftrace_find_call_by_number (btinfo, gap->number - 1);
- if (lhs == NULL || lhs->errcode != 0)
- continue;
- /* Skip gaps to the right. */
- rhs = ftrace_find_call_by_number (btinfo, gap->number + 1);
- while (rhs != NULL && rhs->errcode != 0)
- rhs = ftrace_find_call_by_number (btinfo, rhs->number + 1);
- /* Ignore gaps at the end of the trace. */
- if (rhs == NULL)
- continue;
- bridged = ftrace_bridge_gap (btinfo, lhs, rhs, min_matches);
- /* Keep track of gaps we were not able to bridge and try again.
- If we just pushed them to the end of GAPS we would risk an
- infinite loop in case we simply cannot bridge a gap. */
- if (bridged == 0)
- remaining.push_back (number);
- }
- /* Let's see if we made any progress. */
- if (remaining.size () == gaps.size ())
- break;
- gaps.clear ();
- gaps.swap (remaining);
- }
- /* We get here if either GAPS is empty or if GAPS equals REMAINING. */
- if (gaps.empty ())
- break;
- remaining.clear ();
- }
- /* We may omit this in some cases. Not sure it is worth the extra
- complication, though. */
- ftrace_compute_global_level_offset (btinfo);
- }
- /* Compute the function branch trace from BTS trace. */
- static void
- btrace_compute_ftrace_bts (struct thread_info *tp,
- const struct btrace_data_bts *btrace,
- std::vector<unsigned int> &gaps)
- {
- /* We may end up doing target calls that require the current thread to be TP,
- for example reading memory through gdb_insn_length. Make sure TP is the
- current thread. */
- scoped_restore_current_thread restore_thread;
- switch_to_thread (tp);
- struct btrace_thread_info *btinfo;
- struct gdbarch *gdbarch;
- unsigned int blk;
- int level;
- gdbarch = target_gdbarch ();
- btinfo = &tp->btrace;
- blk = btrace->blocks->size ();
- if (btinfo->functions.empty ())
- level = INT_MAX;
- else
- level = -btinfo->level;
- while (blk != 0)
- {
- CORE_ADDR pc;
- blk -= 1;
- const btrace_block &block = btrace->blocks->at (blk);
- pc = block.begin;
- for (;;)
- {
- struct btrace_function *bfun;
- struct btrace_insn insn;
- int size;
- /* We should hit the end of the block. Warn if we went too far. */
- if (block.end < pc)
- {
- /* Indicate the gap in the trace. */
- bfun = ftrace_new_gap (btinfo, BDE_BTS_OVERFLOW, gaps);
- warning (_("Recorded trace may be corrupted at instruction "
- "%u (pc = %s)."), bfun->insn_offset - 1,
- core_addr_to_string_nz (pc));
- break;
- }
- bfun = ftrace_update_function (btinfo, pc);
- /* Maintain the function level offset.
- For all but the last block, we do it here. */
- if (blk != 0)
- level = std::min (level, bfun->level);
- size = 0;
- try
- {
- size = gdb_insn_length (gdbarch, pc);
- }
- catch (const gdb_exception_error &error)
- {
- }
- insn.pc = pc;
- insn.size = size;
- insn.iclass = ftrace_classify_insn (gdbarch, pc);
- insn.flags = 0;
- ftrace_update_insns (bfun, insn);
- /* We're done once we pushed the instruction at the end. */
- if (block.end == pc)
- break;
- /* We can't continue if we fail to compute the size. */
- if (size <= 0)
- {
- /* Indicate the gap in the trace. We just added INSN so we're
- not at the beginning. */
- bfun = ftrace_new_gap (btinfo, BDE_BTS_INSN_SIZE, gaps);
- warning (_("Recorded trace may be incomplete at instruction %u "
- "(pc = %s)."), bfun->insn_offset - 1,
- core_addr_to_string_nz (pc));
- break;
- }
- pc += size;
- /* Maintain the function level offset.
- For the last block, we do it here to not consider the last
- instruction.
- Since the last instruction corresponds to the current instruction
- and is not really part of the execution history, it shouldn't
- affect the level. */
- if (blk == 0)
- level = std::min (level, bfun->level);
- }
- }
- /* LEVEL is the minimal function level of all btrace function segments.
- Define the global level offset to -LEVEL so all function levels are
- normalized to start at zero. */
- btinfo->level = -level;
- }
- #if defined (HAVE_LIBIPT)
- static enum btrace_insn_class
- pt_reclassify_insn (enum pt_insn_class iclass)
- {
- switch (iclass)
- {
- case ptic_call:
- return BTRACE_INSN_CALL;
- case ptic_return:
- return BTRACE_INSN_RETURN;
- case ptic_jump:
- return BTRACE_INSN_JUMP;
- default:
- return BTRACE_INSN_OTHER;
- }
- }
- /* Return the btrace instruction flags for INSN. */
- static btrace_insn_flags
- pt_btrace_insn_flags (const struct pt_insn &insn)
- {
- btrace_insn_flags flags = 0;
- if (insn.speculative)
- flags |= BTRACE_INSN_FLAG_SPECULATIVE;
- return flags;
- }
- /* Return the btrace instruction for INSN. */
- static btrace_insn
- pt_btrace_insn (const struct pt_insn &insn)
- {
- return {(CORE_ADDR) insn.ip, (gdb_byte) insn.size,
- pt_reclassify_insn (insn.iclass),
- pt_btrace_insn_flags (insn)};
- }
- /* Handle instruction decode events (libipt-v2). */
- static int
- handle_pt_insn_events (struct btrace_thread_info *btinfo,
- struct pt_insn_decoder *decoder,
- std::vector<unsigned int> &gaps, int status)
- {
- #if defined (HAVE_PT_INSN_EVENT)
- while (status & pts_event_pending)
- {
- struct btrace_function *bfun;
- struct pt_event event;
- uint64_t offset;
- status = pt_insn_event (decoder, &event, sizeof (event));
- if (status < 0)
- break;
- switch (event.type)
- {
- default:
- break;
- case ptev_enabled:
- if (event.status_update != 0)
- break;
- if (event.variant.enabled.resumed == 0 && !btinfo->functions.empty ())
- {
- bfun = ftrace_new_gap (btinfo, BDE_PT_DISABLED, gaps);
- pt_insn_get_offset (decoder, &offset);
- warning (_("Non-contiguous trace at instruction %u (offset = 0x%"
- PRIx64 ")."), bfun->insn_offset - 1, offset);
- }
- break;
- case ptev_overflow:
- bfun = ftrace_new_gap (btinfo, BDE_PT_OVERFLOW, gaps);
- pt_insn_get_offset (decoder, &offset);
- warning (_("Overflow at instruction %u (offset = 0x%" PRIx64 ")."),
- bfun->insn_offset - 1, offset);
- break;
- }
- }
- #endif /* defined (HAVE_PT_INSN_EVENT) */
- return status;
- }
- /* Handle events indicated by flags in INSN (libipt-v1). */
- static void
- handle_pt_insn_event_flags (struct btrace_thread_info *btinfo,
- struct pt_insn_decoder *decoder,
- const struct pt_insn &insn,
- std::vector<unsigned int> &gaps)
- {
- #if defined (HAVE_STRUCT_PT_INSN_ENABLED)
- /* Tracing is disabled and re-enabled each time we enter the kernel. Most
- times, we continue from the same instruction we stopped before. This is
- indicated via the RESUMED instruction flag. The ENABLED instruction flag
- means that we continued from some other instruction. Indicate this as a
- trace gap except when tracing just started. */
- if (insn.enabled && !btinfo->functions.empty ())
- {
- struct btrace_function *bfun;
- uint64_t offset;
- bfun = ftrace_new_gap (btinfo, BDE_PT_DISABLED, gaps);
- pt_insn_get_offset (decoder, &offset);
- warning (_("Non-contiguous trace at instruction %u (offset = 0x%" PRIx64
- ", pc = 0x%" PRIx64 ")."), bfun->insn_offset - 1, offset,
- insn.ip);
- }
- #endif /* defined (HAVE_STRUCT_PT_INSN_ENABLED) */
- #if defined (HAVE_STRUCT_PT_INSN_RESYNCED)
- /* Indicate trace overflows. */
- if (insn.resynced)
- {
- struct btrace_function *bfun;
- uint64_t offset;
- bfun = ftrace_new_gap (btinfo, BDE_PT_OVERFLOW, gaps);
- pt_insn_get_offset (decoder, &offset);
- warning (_("Overflow at instruction %u (offset = 0x%" PRIx64 ", pc = 0x%"
- PRIx64 ")."), bfun->insn_offset - 1, offset, insn.ip);
- }
- #endif /* defined (HAVE_STRUCT_PT_INSN_RESYNCED) */
- }
- /* Add function branch trace to BTINFO using DECODER. */
- static void
- ftrace_add_pt (struct btrace_thread_info *btinfo,
- struct pt_insn_decoder *decoder,
- int *plevel,
- std::vector<unsigned int> &gaps)
- {
- struct btrace_function *bfun;
- uint64_t offset;
- int status;
- for (;;)
- {
- struct pt_insn insn;
- status = pt_insn_sync_forward (decoder);
- if (status < 0)
- {
- if (status != -pte_eos)
- warning (_("Failed to synchronize onto the Intel Processor "
- "Trace stream: %s."), pt_errstr (pt_errcode (status)));
- break;
- }
- for (;;)
- {
- /* Handle events from the previous iteration or synchronization. */
- status = handle_pt_insn_events (btinfo, decoder, gaps, status);
- if (status < 0)
- break;
- status = pt_insn_next (decoder, &insn, sizeof(insn));
- if (status < 0)
- break;
- /* Handle events indicated by flags in INSN. */
- handle_pt_insn_event_flags (btinfo, decoder, insn, gaps);
- bfun = ftrace_update_function (btinfo, insn.ip);
- /* Maintain the function level offset. */
- *plevel = std::min (*plevel, bfun->level);
- ftrace_update_insns (bfun, pt_btrace_insn (insn));
- }
- if (status == -pte_eos)
- break;
- /* Indicate the gap in the trace. */
- bfun = ftrace_new_gap (btinfo, status, gaps);
- pt_insn_get_offset (decoder, &offset);
- warning (_("Decode error (%d) at instruction %u (offset = 0x%" PRIx64
- ", pc = 0x%" PRIx64 "): %s."), status, bfun->insn_offset - 1,
- offset, insn.ip, pt_errstr (pt_errcode (status)));
- }
- }
- /* A callback function to allow the trace decoder to read the inferior's
- memory. */
- static int
- btrace_pt_readmem_callback (gdb_byte *buffer, size_t size,
- const struct pt_asid *asid, uint64_t pc,
- void *context)
- {
- int result, errcode;
- result = (int) size;
- try
- {
- errcode = target_read_code ((CORE_ADDR) pc, buffer, size);
- if (errcode != 0)
- result = -pte_nomap;
- }
- catch (const gdb_exception_error &error)
- {
- result = -pte_nomap;
- }
- return result;
- }
- /* Translate the vendor from one enum to another. */
- static enum pt_cpu_vendor
- pt_translate_cpu_vendor (enum btrace_cpu_vendor vendor)
- {
- switch (vendor)
- {
- default:
- return pcv_unknown;
- case CV_INTEL:
- return pcv_intel;
- }
- }
- /* Finalize the function branch trace after decode. */
- static void btrace_finalize_ftrace_pt (struct pt_insn_decoder *decoder,
- struct thread_info *tp, int level)
- {
- pt_insn_free_decoder (decoder);
- /* LEVEL is the minimal function level of all btrace function segments.
- Define the global level offset to -LEVEL so all function levels are
- normalized to start at zero. */
- tp->btrace.level = -level;
- /* Add a single last instruction entry for the current PC.
- This allows us to compute the backtrace at the current PC using both
- standard unwind and btrace unwind.
- This extra entry is ignored by all record commands. */
- btrace_add_pc (tp);
- }
- /* Compute the function branch trace from Intel Processor Trace
- format. */
- static void
- btrace_compute_ftrace_pt (struct thread_info *tp,
- const struct btrace_data_pt *btrace,
- std::vector<unsigned int> &gaps)
- {
- /* We may end up doing target calls that require the current thread to be TP,
- for example reading memory through btrace_pt_readmem_callback. Make sure
- TP is the current thread. */
- scoped_restore_current_thread restore_thread;
- switch_to_thread (tp);
- struct btrace_thread_info *btinfo;
- struct pt_insn_decoder *decoder;
- struct pt_config config;
- int level, errcode;
- if (btrace->size == 0)
- return;
- btinfo = &tp->btrace;
- if (btinfo->functions.empty ())
- level = INT_MAX;
- else
- level = -btinfo->level;
- pt_config_init(&config);
- config.begin = btrace->data;
- config.end = btrace->data + btrace->size;
- /* We treat an unknown vendor as 'no errata'. */
- if (btrace->config.cpu.vendor != CV_UNKNOWN)
- {
- config.cpu.vendor
- = pt_translate_cpu_vendor (btrace->config.cpu.vendor);
- config.cpu.family = btrace->config.cpu.family;
- config.cpu.model = btrace->config.cpu.model;
- config.cpu.stepping = btrace->config.cpu.stepping;
- errcode = pt_cpu_errata (&config.errata, &config.cpu);
- if (errcode < 0)
- error (_("Failed to configure the Intel Processor Trace "
- "decoder: %s."), pt_errstr (pt_errcode (errcode)));
- }
- decoder = pt_insn_alloc_decoder (&config);
- if (decoder == NULL)
- error (_("Failed to allocate the Intel Processor Trace decoder."));
- try
- {
- struct pt_image *image;
- image = pt_insn_get_image(decoder);
- if (image == NULL)
- error (_("Failed to configure the Intel Processor Trace decoder."));
- errcode = pt_image_set_callback(image, btrace_pt_readmem_callback, NULL);
- if (errcode < 0)
- error (_("Failed to configure the Intel Processor Trace decoder: "
- "%s."), pt_errstr (pt_errcode (errcode)));
- ftrace_add_pt (btinfo, decoder, &level, gaps);
- }
- catch (const gdb_exception &error)
- {
- /* Indicate a gap in the trace if we quit trace processing. */
- if (error.reason == RETURN_QUIT && !btinfo->functions.empty ())
- ftrace_new_gap (btinfo, BDE_PT_USER_QUIT, gaps);
- btrace_finalize_ftrace_pt (decoder, tp, level);
- throw;
- }
- btrace_finalize_ftrace_pt (decoder, tp, level);
- }
- #else /* defined (HAVE_LIBIPT) */
- static void
- btrace_compute_ftrace_pt (struct thread_info *tp,
- const struct btrace_data_pt *btrace,
- std::vector<unsigned int> &gaps)
- {
- internal_error (__FILE__, __LINE__, _("Unexpected branch trace format."));
- }
- #endif /* defined (HAVE_LIBIPT) */
- /* Compute the function branch trace from a block branch trace BTRACE for
- a thread given by BTINFO. If CPU is not NULL, overwrite the cpu in the
- branch trace configuration. This is currently only used for the PT
- format. */
- static void
- btrace_compute_ftrace_1 (struct thread_info *tp,
- struct btrace_data *btrace,
- const struct btrace_cpu *cpu,
- std::vector<unsigned int> &gaps)
- {
- DEBUG ("compute ftrace");
- switch (btrace->format)
- {
- case BTRACE_FORMAT_NONE:
- return;
- case BTRACE_FORMAT_BTS:
- btrace_compute_ftrace_bts (tp, &btrace->variant.bts, gaps);
- return;
- case BTRACE_FORMAT_PT:
- /* Overwrite the cpu we use for enabling errata workarounds. */
- if (cpu != nullptr)
- btrace->variant.pt.config.cpu = *cpu;
- btrace_compute_ftrace_pt (tp, &btrace->variant.pt, gaps);
- return;
- }
- internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
- }
- static void
- btrace_finalize_ftrace (struct thread_info *tp, std::vector<unsigned int> &gaps)
- {
- if (!gaps.empty ())
- {
- tp->btrace.ngaps += gaps.size ();
- btrace_bridge_gaps (tp, gaps);
- }
- }
- static void
- btrace_compute_ftrace (struct thread_info *tp, struct btrace_data *btrace,
- const struct btrace_cpu *cpu)
- {
- std::vector<unsigned int> gaps;
- try
- {
- btrace_compute_ftrace_1 (tp, btrace, cpu, gaps);
- }
- catch (const gdb_exception &error)
- {
- btrace_finalize_ftrace (tp, gaps);
- throw;
- }
- btrace_finalize_ftrace (tp, gaps);
- }
- /* Add an entry for the current PC. */
- static void
- btrace_add_pc (struct thread_info *tp)
- {
- struct btrace_data btrace;
- struct regcache *regcache;
- CORE_ADDR pc;
- regcache = get_thread_regcache (tp);
- pc = regcache_read_pc (regcache);
- btrace.format = BTRACE_FORMAT_BTS;
- btrace.variant.bts.blocks = new std::vector<btrace_block>;
- btrace.variant.bts.blocks->emplace_back (pc, pc);
- btrace_compute_ftrace (tp, &btrace, NULL);
- }
- /* See btrace.h. */
- void
- btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
- {
- if (tp->btrace.target != NULL)
- error (_("Recording already enabled on thread %s (%s)."),
- print_thread_id (tp), target_pid_to_str (tp->ptid).c_str ());
- #if !defined (HAVE_LIBIPT)
- if (conf->format == BTRACE_FORMAT_PT)
- error (_("Intel Processor Trace support was disabled at compile time."));
- #endif /* !defined (HAVE_LIBIPT) */
- DEBUG ("enable thread %s (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str ());
- tp->btrace.target = target_enable_btrace (tp, conf);
- if (tp->btrace.target == NULL)
- error (_("Failed to enable recording on thread %s (%s)."),
- print_thread_id (tp), target_pid_to_str (tp->ptid).c_str ());
- /* We need to undo the enable in case of errors. */
- try
- {
- /* Add an entry for the current PC so we start tracing from where we
- enabled it.
- If we can't access TP's registers, TP is most likely running. In this
- case, we can't really say where tracing was enabled so it should be
- safe to simply skip this step.
- This is not relevant for BTRACE_FORMAT_PT since the trace will already
- start at the PC at which tracing was enabled. */
- if (conf->format != BTRACE_FORMAT_PT
- && can_access_registers_thread (tp))
- btrace_add_pc (tp);
- }
- catch (const gdb_exception &exception)
- {
- btrace_disable (tp);
- throw;
- }
- }
- /* See btrace.h. */
- const struct btrace_config *
- btrace_conf (const struct btrace_thread_info *btinfo)
- {
- if (btinfo->target == NULL)
- return NULL;
- return target_btrace_conf (btinfo->target);
- }
- /* See btrace.h. */
- void
- btrace_disable (struct thread_info *tp)
- {
- struct btrace_thread_info *btp = &tp->btrace;
- if (btp->target == NULL)
- error (_("Recording not enabled on thread %s (%s)."),
- print_thread_id (tp), target_pid_to_str (tp->ptid).c_str ());
- DEBUG ("disable thread %s (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str ());
- target_disable_btrace (btp->target);
- btp->target = NULL;
- btrace_clear (tp);
- }
- /* See btrace.h. */
- void
- btrace_teardown (struct thread_info *tp)
- {
- struct btrace_thread_info *btp = &tp->btrace;
- if (btp->target == NULL)
- return;
- DEBUG ("teardown thread %s (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str ());
- target_teardown_btrace (btp->target);
- btp->target = NULL;
- btrace_clear (tp);
- }
- /* Stitch branch trace in BTS format. */
- static int
- btrace_stitch_bts (struct btrace_data_bts *btrace, struct thread_info *tp)
- {
- struct btrace_thread_info *btinfo;
- struct btrace_function *last_bfun;
- btrace_block *first_new_block;
- btinfo = &tp->btrace;
- gdb_assert (!btinfo->functions.empty ());
- gdb_assert (!btrace->blocks->empty ());
- last_bfun = &btinfo->functions.back ();
- /* If the existing trace ends with a gap, we just glue the traces
- together. We need to drop the last (i.e. chronologically first) block
- of the new trace, though, since we can't fill in the start address.*/
- if (last_bfun->insn.empty ())
- {
- btrace->blocks->pop_back ();
- return 0;
- }
- /* Beware that block trace starts with the most recent block, so the
- chronologically first block in the new trace is the last block in
- the new trace's block vector. */
- first_new_block = &btrace->blocks->back ();
- const btrace_insn &last_insn = last_bfun->insn.back ();
- /* If the current PC at the end of the block is the same as in our current
- trace, there are two explanations:
- 1. we executed the instruction and some branch brought us back.
- 2. we have not made any progress.
- In the first case, the delta trace vector should contain at least two
- entries.
- In the second case, the delta trace vector should contain exactly one
- entry for the partial block containing the current PC. Remove it. */
- if (first_new_block->end == last_insn.pc && btrace->blocks->size () == 1)
- {
- btrace->blocks->pop_back ();
- return 0;
- }
- DEBUG ("stitching %s to %s", ftrace_print_insn_addr (&last_insn),
- core_addr_to_string_nz (first_new_block->end));
- /* Do a simple sanity check to make sure we don't accidentally end up
- with a bad block. This should not occur in practice. */
- if (first_new_block->end < last_insn.pc)
- {
- warning (_("Error while trying to read delta trace. Falling back to "
- "a full read."));
- return -1;
- }
- /* We adjust the last block to start at the end of our current trace. */
- gdb_assert (first_new_block->begin == 0);
- first_new_block->begin = last_insn.pc;
- /* We simply pop the last insn so we can insert it again as part of
- the normal branch trace computation.
- Since instruction iterators are based on indices in the instructions
- vector, we don't leave any pointers dangling. */
- DEBUG ("pruning insn at %s for stitching",
- ftrace_print_insn_addr (&last_insn));
- last_bfun->insn.pop_back ();
- /* The instructions vector may become empty temporarily if this has
- been the only instruction in this function segment.
- This violates the invariant but will be remedied shortly by
- btrace_compute_ftrace when we add the new trace. */
- /* The only case where this would hurt is if the entire trace consisted
- of just that one instruction. If we remove it, we might turn the now
- empty btrace function segment into a gap. But we don't want gaps at
- the beginning. To avoid this, we remove the entire old trace. */
- if (last_bfun->number == 1 && last_bfun->insn.empty ())
- btrace_clear (tp);
- return 0;
- }
- /* Adjust the block trace in order to stitch old and new trace together.
- BTRACE is the new delta trace between the last and the current stop.
- TP is the traced thread.
- May modifx BTRACE as well as the existing trace in TP.
- Return 0 on success, -1 otherwise. */
- static int
- btrace_stitch_trace (struct btrace_data *btrace, struct thread_info *tp)
- {
- /* If we don't have trace, there's nothing to do. */
- if (btrace->empty ())
- return 0;
- switch (btrace->format)
- {
- case BTRACE_FORMAT_NONE:
- return 0;
- case BTRACE_FORMAT_BTS:
- return btrace_stitch_bts (&btrace->variant.bts, tp);
- case BTRACE_FORMAT_PT:
- /* Delta reads are not supported. */
- return -1;
- }
- internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
- }
- /* Clear the branch trace histories in BTINFO. */
- static void
- btrace_clear_history (struct btrace_thread_info *btinfo)
- {
- xfree (btinfo->insn_history);
- xfree (btinfo->call_history);
- xfree (btinfo->replay);
- btinfo->insn_history = NULL;
- btinfo->call_history = NULL;
- btinfo->replay = NULL;
- }
- /* Clear the branch trace maintenance histories in BTINFO. */
- static void
- btrace_maint_clear (struct btrace_thread_info *btinfo)
- {
- switch (btinfo->data.format)
- {
- default:
- break;
- case BTRACE_FORMAT_BTS:
- btinfo->maint.variant.bts.packet_history.begin = 0;
- btinfo->maint.variant.bts.packet_history.end = 0;
- break;
- #if defined (HAVE_LIBIPT)
- case BTRACE_FORMAT_PT:
- delete btinfo->maint.variant.pt.packets;
- btinfo->maint.variant.pt.packets = NULL;
- btinfo->maint.variant.pt.packet_history.begin = 0;
- btinfo->maint.variant.pt.packet_history.end = 0;
- break;
- #endif /* defined (HAVE_LIBIPT) */
- }
- }
- /* See btrace.h. */
- const char *
- btrace_decode_error (enum btrace_format format, int errcode)
- {
- switch (format)
- {
- case BTRACE_FORMAT_BTS:
- switch (errcode)
- {
- case BDE_BTS_OVERFLOW:
- return _("instruction overflow");
- case BDE_BTS_INSN_SIZE:
- return _("unknown instruction");
- default:
- break;
- }
- break;
- #if defined (HAVE_LIBIPT)
- case BTRACE_FORMAT_PT:
- switch (errcode)
- {
- case BDE_PT_USER_QUIT:
- return _("trace decode cancelled");
- case BDE_PT_DISABLED:
- return _("disabled");
- case BDE_PT_OVERFLOW:
- return _("overflow");
- default:
- if (errcode < 0)
- return pt_errstr (pt_errcode (errcode));
- break;
- }
- break;
- #endif /* defined (HAVE_LIBIPT) */
- default:
- break;
- }
- return _("unknown");
- }
- /* See btrace.h. */
- void
- btrace_fetch (struct thread_info *tp, const struct btrace_cpu *cpu)
- {
- struct btrace_thread_info *btinfo;
- struct btrace_target_info *tinfo;
- struct btrace_data btrace;
- int errcode;
- DEBUG ("fetch thread %s (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str ());
- btinfo = &tp->btrace;
- tinfo = btinfo->target;
- if (tinfo == NULL)
- return;
- /* There's no way we could get new trace while replaying.
- On the other hand, delta trace would return a partial record with the
- current PC, which is the replay PC, not the last PC, as expected. */
- if (btinfo->replay != NULL)
- return;
- /* With CLI usage, TP is always the current thread when we get here.
- However, since we can also store a gdb.Record object in Python
- referring to a different thread than the current one, we need to
- temporarily set the current thread. */
- scoped_restore_current_thread restore_thread;
- switch_to_thread (tp);
- /* We should not be called on running or exited threads. */
- gdb_assert (can_access_registers_thread (tp));
- /* Let's first try to extend the trace we already have. */
- if (!btinfo->functions.empty ())
- {
- errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_DELTA);
- if (errcode == 0)
- {
- /* Success. Let's try to stitch the traces together. */
- errcode = btrace_stitch_trace (&btrace, tp);
- }
- else
- {
- /* We failed to read delta trace. Let's try to read new trace. */
- errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_NEW);
- /* If we got any new trace, discard what we have. */
- if (errcode == 0 && !btrace.empty ())
- btrace_clear (tp);
- }
- /* If we were not able to read the trace, we start over. */
- if (errcode != 0)
- {
- btrace_clear (tp);
- errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_ALL);
- }
- }
- else
- errcode = target_read_btrace (&btrace, tinfo, BTRACE_READ_ALL);
- /* If we were not able to read the branch trace, signal an error. */
- if (errcode != 0)
- error (_("Failed to read branch trace."));
- /* Compute the trace, provided we have any. */
- if (!btrace.empty ())
- {
- /* Store the raw trace data. The stored data will be cleared in
- btrace_clear, so we always append the new trace. */
- btrace_data_append (&btinfo->data, &btrace);
- btrace_maint_clear (btinfo);
- btrace_clear_history (btinfo);
- btrace_compute_ftrace (tp, &btrace, cpu);
- }
- }
- /* See btrace.h. */
- void
- btrace_clear (struct thread_info *tp)
- {
- struct btrace_thread_info *btinfo;
- DEBUG ("clear thread %s (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str ());
- /* Make sure btrace frames that may hold a pointer into the branch
- trace data are destroyed. */
- reinit_frame_cache ();
- btinfo = &tp->btrace;
- btinfo->functions.clear ();
- btinfo->ngaps = 0;
- /* Must clear the maint data before - it depends on BTINFO->DATA. */
- btrace_maint_clear (btinfo);
- btinfo->data.clear ();
- btrace_clear_history (btinfo);
- }
- /* See btrace.h. */
- void
- btrace_free_objfile (struct objfile *objfile)
- {
- DEBUG ("free objfile");
- for (thread_info *tp : all_non_exited_threads ())
- btrace_clear (tp);
- }
- #if defined (HAVE_LIBEXPAT)
- /* Check the btrace document version. */
- static void
- check_xml_btrace_version (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- const char *version
- = (const char *) xml_find_attribute (attributes, "version")->value.get ();
- if (strcmp (version, "1.0") != 0)
- gdb_xml_error (parser, _("Unsupported btrace version: \"%s\""), version);
- }
- /* Parse a btrace "block" xml record. */
- static void
- parse_xml_btrace_block (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct btrace_data *btrace;
- ULONGEST *begin, *end;
- btrace = (struct btrace_data *) user_data;
- switch (btrace->format)
- {
- case BTRACE_FORMAT_BTS:
- break;
- case BTRACE_FORMAT_NONE:
- btrace->format = BTRACE_FORMAT_BTS;
- btrace->variant.bts.blocks = new std::vector<btrace_block>;
- break;
- default:
- gdb_xml_error (parser, _("Btrace format error."));
- }
- begin = (ULONGEST *) xml_find_attribute (attributes, "begin")->value.get ();
- end = (ULONGEST *) xml_find_attribute (attributes, "end")->value.get ();
- btrace->variant.bts.blocks->emplace_back (*begin, *end);
- }
- /* Parse a "raw" xml record. */
- static void
- parse_xml_raw (struct gdb_xml_parser *parser, const char *body_text,
- gdb_byte **pdata, size_t *psize)
- {
- gdb_byte *bin;
- size_t len, size;
- len = strlen (body_text);
- if (len % 2 != 0)
- gdb_xml_error (parser, _("Bad raw data size."));
- size = len / 2;
- gdb::unique_xmalloc_ptr<gdb_byte> data ((gdb_byte *) xmalloc (size));
- bin = data.get ();
- /* We use hex encoding - see gdbsupport/rsp-low.h. */
- while (len > 0)
- {
- char hi, lo;
- hi = *body_text++;
- lo = *body_text++;
- if (hi == 0 || lo == 0)
- gdb_xml_error (parser, _("Bad hex encoding."));
- *bin++ = fromhex (hi) * 16 + fromhex (lo);
- len -= 2;
- }
- *pdata = data.release ();
- *psize = size;
- }
- /* Parse a btrace pt-config "cpu" xml record. */
- static void
- parse_xml_btrace_pt_config_cpu (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct btrace_data *btrace;
- const char *vendor;
- ULONGEST *family, *model, *stepping;
- vendor =
- (const char *) xml_find_attribute (attributes, "vendor")->value.get ();
- family
- = (ULONGEST *) xml_find_attribute (attributes, "family")->value.get ();
- model
- = (ULONGEST *) xml_find_attribute (attributes, "model")->value.get ();
- stepping
- = (ULONGEST *) xml_find_attribute (attributes, "stepping")->value.get ();
- btrace = (struct btrace_data *) user_data;
- if (strcmp (vendor, "GenuineIntel") == 0)
- btrace->variant.pt.config.cpu.vendor = CV_INTEL;
- btrace->variant.pt.config.cpu.family = *family;
- btrace->variant.pt.config.cpu.model = *model;
- btrace->variant.pt.config.cpu.stepping = *stepping;
- }
- /* Parse a btrace pt "raw" xml record. */
- static void
- parse_xml_btrace_pt_raw (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data, const char *body_text)
- {
- struct btrace_data *btrace;
- btrace = (struct btrace_data *) user_data;
- parse_xml_raw (parser, body_text, &btrace->variant.pt.data,
- &btrace->variant.pt.size);
- }
- /* Parse a btrace "pt" xml record. */
- static void
- parse_xml_btrace_pt (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct btrace_data *btrace;
- btrace = (struct btrace_data *) user_data;
- btrace->format = BTRACE_FORMAT_PT;
- btrace->variant.pt.config.cpu.vendor = CV_UNKNOWN;
- btrace->variant.pt.data = NULL;
- btrace->variant.pt.size = 0;
- }
- static const struct gdb_xml_attribute block_attributes[] = {
- { "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_attribute btrace_pt_config_cpu_attributes[] = {
- { "vendor", GDB_XML_AF_NONE, NULL, NULL },
- { "family", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { "model", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { "stepping", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element btrace_pt_config_children[] = {
- { "cpu", btrace_pt_config_cpu_attributes, NULL, GDB_XML_EF_OPTIONAL,
- parse_xml_btrace_pt_config_cpu, NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element btrace_pt_children[] = {
- { "pt-config", NULL, btrace_pt_config_children, GDB_XML_EF_OPTIONAL, NULL,
- NULL },
- { "raw", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL, parse_xml_btrace_pt_raw },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_attribute btrace_attributes[] = {
- { "version", GDB_XML_AF_NONE, NULL, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element btrace_children[] = {
- { "block", block_attributes, NULL,
- GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, parse_xml_btrace_block, NULL },
- { "pt", NULL, btrace_pt_children, GDB_XML_EF_OPTIONAL, parse_xml_btrace_pt,
- NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element btrace_elements[] = {
- { "btrace", btrace_attributes, btrace_children, GDB_XML_EF_NONE,
- check_xml_btrace_version, NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- #endif /* defined (HAVE_LIBEXPAT) */
- /* See btrace.h. */
- void
- parse_xml_btrace (struct btrace_data *btrace, const char *buffer)
- {
- #if defined (HAVE_LIBEXPAT)
- int errcode;
- btrace_data result;
- result.format = BTRACE_FORMAT_NONE;
- errcode = gdb_xml_parse_quick (_("btrace"), "btrace.dtd", btrace_elements,
- buffer, &result);
- if (errcode != 0)
- error (_("Error parsing branch trace."));
- /* Keep parse results. */
- *btrace = std::move (result);
- #else /* !defined (HAVE_LIBEXPAT) */
- error (_("Cannot process branch trace. XML support was disabled at "
- "compile time."));
- #endif /* !defined (HAVE_LIBEXPAT) */
- }
- #if defined (HAVE_LIBEXPAT)
- /* Parse a btrace-conf "bts" xml record. */
- static void
- parse_xml_btrace_conf_bts (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct btrace_config *conf;
- struct gdb_xml_value *size;
- conf = (struct btrace_config *) user_data;
- conf->format = BTRACE_FORMAT_BTS;
- conf->bts.size = 0;
- size = xml_find_attribute (attributes, "size");
- if (size != NULL)
- conf->bts.size = (unsigned int) *(ULONGEST *) size->value.get ();
- }
- /* Parse a btrace-conf "pt" xml record. */
- static void
- parse_xml_btrace_conf_pt (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct btrace_config *conf;
- struct gdb_xml_value *size;
- conf = (struct btrace_config *) user_data;
- conf->format = BTRACE_FORMAT_PT;
- conf->pt.size = 0;
- size = xml_find_attribute (attributes, "size");
- if (size != NULL)
- conf->pt.size = (unsigned int) *(ULONGEST *) size->value.get ();
- }
- static const struct gdb_xml_attribute btrace_conf_pt_attributes[] = {
- { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_attribute btrace_conf_bts_attributes[] = {
- { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element btrace_conf_children[] = {
- { "bts", btrace_conf_bts_attributes, NULL, GDB_XML_EF_OPTIONAL,
- parse_xml_btrace_conf_bts, NULL },
- { "pt", btrace_conf_pt_attributes, NULL, GDB_XML_EF_OPTIONAL,
- parse_xml_btrace_conf_pt, NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_attribute btrace_conf_attributes[] = {
- { "version", GDB_XML_AF_NONE, NULL, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element btrace_conf_elements[] = {
- { "btrace-conf", btrace_conf_attributes, btrace_conf_children,
- GDB_XML_EF_NONE, NULL, NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- #endif /* defined (HAVE_LIBEXPAT) */
- /* See btrace.h. */
- void
- parse_xml_btrace_conf (struct btrace_config *conf, const char *xml)
- {
- #if defined (HAVE_LIBEXPAT)
- int errcode;
- errcode = gdb_xml_parse_quick (_("btrace-conf"), "btrace-conf.dtd",
- btrace_conf_elements, xml, conf);
- if (errcode != 0)
- error (_("Error parsing branch trace configuration."));
- #else /* !defined (HAVE_LIBEXPAT) */
- error (_("Cannot process the branch trace configuration. XML support "
- "was disabled at compile time."));
- #endif /* !defined (HAVE_LIBEXPAT) */
- }
- /* See btrace.h. */
- const struct btrace_insn *
- btrace_insn_get (const struct btrace_insn_iterator *it)
- {
- const struct btrace_function *bfun;
- unsigned int index, end;
- index = it->insn_index;
- bfun = &it->btinfo->functions[it->call_index];
- /* Check if the iterator points to a gap in the trace. */
- if (bfun->errcode != 0)
- return NULL;
- /* The index is within the bounds of this function's instruction vector. */
- end = bfun->insn.size ();
- gdb_assert (0 < end);
- gdb_assert (index < end);
- return &bfun->insn[index];
- }
- /* See btrace.h. */
- int
- btrace_insn_get_error (const struct btrace_insn_iterator *it)
- {
- return it->btinfo->functions[it->call_index].errcode;
- }
- /* See btrace.h. */
- unsigned int
- btrace_insn_number (const struct btrace_insn_iterator *it)
- {
- return it->btinfo->functions[it->call_index].insn_offset + it->insn_index;
- }
- /* See btrace.h. */
- void
- btrace_insn_begin (struct btrace_insn_iterator *it,
- const struct btrace_thread_info *btinfo)
- {
- if (btinfo->functions.empty ())
- error (_("No trace."));
- it->btinfo = btinfo;
- it->call_index = 0;
- it->insn_index = 0;
- }
- /* See btrace.h. */
- void
- btrace_insn_end (struct btrace_insn_iterator *it,
- const struct btrace_thread_info *btinfo)
- {
- const struct btrace_function *bfun;
- unsigned int length;
- if (btinfo->functions.empty ())
- error (_("No trace."));
- bfun = &btinfo->functions.back ();
- length = bfun->insn.size ();
- /* The last function may either be a gap or it contains the current
- instruction, which is one past the end of the execution trace; ignore
- it. */
- if (length > 0)
- length -= 1;
- it->btinfo = btinfo;
- it->call_index = bfun->number - 1;
- it->insn_index = length;
- }
- /* See btrace.h. */
- unsigned int
- btrace_insn_next (struct btrace_insn_iterator *it, unsigned int stride)
- {
- const struct btrace_function *bfun;
- unsigned int index, steps;
- bfun = &it->btinfo->functions[it->call_index];
- steps = 0;
- index = it->insn_index;
- while (stride != 0)
- {
- unsigned int end, space, adv;
- end = bfun->insn.size ();
- /* An empty function segment represents a gap in the trace. We count
- it as one instruction. */
- if (end == 0)
- {
- const struct btrace_function *next;
- next = ftrace_find_call_by_number (it->btinfo, bfun->number + 1);
- if (next == NULL)
- break;
- stride -= 1;
- steps += 1;
- bfun = next;
- index = 0;
- continue;
- }
- gdb_assert (0 < end);
- gdb_assert (index < end);
- /* Compute the number of instructions remaining in this segment. */
- space = end - index;
- /* Advance the iterator as far as possible within this segment. */
- adv = std::min (space, stride);
- stride -= adv;
- index += adv;
- steps += adv;
- /* Move to the next function if we're at the end of this one. */
- if (index == end)
- {
- const struct btrace_function *next;
- next = ftrace_find_call_by_number (it->btinfo, bfun->number + 1);
- if (next == NULL)
- {
- /* We stepped past the last function.
- Let's adjust the index to point to the last instruction in
- the previous function. */
- index -= 1;
- steps -= 1;
- break;
- }
- /* We now point to the first instruction in the new function. */
- bfun = next;
- index = 0;
- }
- /* We did make progress. */
- gdb_assert (adv > 0);
- }
- /* Update the iterator. */
- it->call_index = bfun->number - 1;
- it->insn_index = index;
- return steps;
- }
- /* See btrace.h. */
- unsigned int
- btrace_insn_prev (struct btrace_insn_iterator *it, unsigned int stride)
- {
- const struct btrace_function *bfun;
- unsigned int index, steps;
- bfun = &it->btinfo->functions[it->call_index];
- steps = 0;
- index = it->insn_index;
- while (stride != 0)
- {
- unsigned int adv;
- /* Move to the previous function if we're at the start of this one. */
- if (index == 0)
- {
- const struct btrace_function *prev;
- prev = ftrace_find_call_by_number (it->btinfo, bfun->number - 1);
- if (prev == NULL)
- break;
- /* We point to one after the last instruction in the new function. */
- bfun = prev;
- index = bfun->insn.size ();
- /* An empty function segment represents a gap in the trace. We count
- it as one instruction. */
- if (index == 0)
- {
- stride -= 1;
- steps += 1;
- continue;
- }
- }
- /* Advance the iterator as far as possible within this segment. */
- adv = std::min (index, stride);
- stride -= adv;
- index -= adv;
- steps += adv;
- /* We did make progress. */
- gdb_assert (adv > 0);
- }
- /* Update the iterator. */
- it->call_index = bfun->number - 1;
- it->insn_index = index;
- return steps;
- }
- /* See btrace.h. */
- int
- btrace_insn_cmp (const struct btrace_insn_iterator *lhs,
- const struct btrace_insn_iterator *rhs)
- {
- gdb_assert (lhs->btinfo == rhs->btinfo);
- if (lhs->call_index != rhs->call_index)
- return lhs->call_index - rhs->call_index;
- return lhs->insn_index - rhs->insn_index;
- }
- /* See btrace.h. */
- int
- btrace_find_insn_by_number (struct btrace_insn_iterator *it,
- const struct btrace_thread_info *btinfo,
- unsigned int number)
- {
- const struct btrace_function *bfun;
- unsigned int upper, lower;
- if (btinfo->functions.empty ())
- return 0;
- lower = 0;
- bfun = &btinfo->functions[lower];
- if (number < bfun->insn_offset)
- return 0;
- upper = btinfo->functions.size () - 1;
- bfun = &btinfo->functions[upper];
- if (number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
- return 0;
- /* We assume that there are no holes in the numbering. */
- for (;;)
- {
- const unsigned int average = lower + (upper - lower) / 2;
- bfun = &btinfo->functions[average];
- if (number < bfun->insn_offset)
- {
- upper = average - 1;
- continue;
- }
- if (number >= bfun->insn_offset + ftrace_call_num_insn (bfun))
- {
- lower = average + 1;
- continue;
- }
- break;
- }
- it->btinfo = btinfo;
- it->call_index = bfun->number - 1;
- it->insn_index = number - bfun->insn_offset;
- return 1;
- }
- /* Returns true if the recording ends with a function segment that
- contains only a single (i.e. the current) instruction. */
- static bool
- btrace_ends_with_single_insn (const struct btrace_thread_info *btinfo)
- {
- const btrace_function *bfun;
- if (btinfo->functions.empty ())
- return false;
- bfun = &btinfo->functions.back ();
- if (bfun->errcode != 0)
- return false;
- return ftrace_call_num_insn (bfun) == 1;
- }
- /* See btrace.h. */
- const struct btrace_function *
- btrace_call_get (const struct btrace_call_iterator *it)
- {
- if (it->index >= it->btinfo->functions.size ())
- return NULL;
- return &it->btinfo->functions[it->index];
- }
- /* See btrace.h. */
- unsigned int
- btrace_call_number (const struct btrace_call_iterator *it)
- {
- const unsigned int length = it->btinfo->functions.size ();
- /* If the last function segment contains only a single instruction (i.e. the
- current instruction), skip it. */
- if ((it->index == length) && btrace_ends_with_single_insn (it->btinfo))
- return length;
- return it->index + 1;
- }
- /* See btrace.h. */
- void
- btrace_call_begin (struct btrace_call_iterator *it,
- const struct btrace_thread_info *btinfo)
- {
- if (btinfo->functions.empty ())
- error (_("No trace."));
- it->btinfo = btinfo;
- it->index = 0;
- }
- /* See btrace.h. */
- void
- btrace_call_end (struct btrace_call_iterator *it,
- const struct btrace_thread_info *btinfo)
- {
- if (btinfo->functions.empty ())
- error (_("No trace."));
- it->btinfo = btinfo;
- it->index = btinfo->functions.size ();
- }
- /* See btrace.h. */
- unsigned int
- btrace_call_next (struct btrace_call_iterator *it, unsigned int stride)
- {
- const unsigned int length = it->btinfo->functions.size ();
- if (it->index + stride < length - 1)
- /* Default case: Simply advance the iterator. */
- it->index += stride;
- else if (it->index + stride == length - 1)
- {
- /* We land exactly at the last function segment. If it contains only one
- instruction (i.e. the current instruction) it is not actually part of
- the trace. */
- if (btrace_ends_with_single_insn (it->btinfo))
- it->index = length;
- else
- it->index = length - 1;
- }
- else
- {
- /* We land past the last function segment and have to adjust the stride.
- If the last function segment contains only one instruction (i.e. the
- current instruction) it is not actually part of the trace. */
- if (btrace_ends_with_single_insn (it->btinfo))
- stride = length - it->index - 1;
- else
- stride = length - it->index;
- it->index = length;
- }
- return stride;
- }
- /* See btrace.h. */
- unsigned int
- btrace_call_prev (struct btrace_call_iterator *it, unsigned int stride)
- {
- const unsigned int length = it->btinfo->functions.size ();
- int steps = 0;
- gdb_assert (it->index <= length);
- if (stride == 0 || it->index == 0)
- return 0;
- /* If we are at the end, the first step is a special case. If the last
- function segment contains only one instruction (i.e. the current
- instruction) it is not actually part of the trace. To be able to step
- over this instruction, we need at least one more function segment. */
- if ((it->index == length) && (length > 1))
- {
- if (btrace_ends_with_single_insn (it->btinfo))
- it->index = length - 2;
- else
- it->index = length - 1;
- steps = 1;
- stride -= 1;
- }
- stride = std::min (stride, it->index);
- it->index -= stride;
- return steps + stride;
- }
- /* See btrace.h. */
- int
- btrace_call_cmp (const struct btrace_call_iterator *lhs,
- const struct btrace_call_iterator *rhs)
- {
- gdb_assert (lhs->btinfo == rhs->btinfo);
- return (int) (lhs->index - rhs->index);
- }
- /* See btrace.h. */
- int
- btrace_find_call_by_number (struct btrace_call_iterator *it,
- const struct btrace_thread_info *btinfo,
- unsigned int number)
- {
- const unsigned int length = btinfo->functions.size ();
- if ((number == 0) || (number > length))
- return 0;
- it->btinfo = btinfo;
- it->index = number - 1;
- return 1;
- }
- /* See btrace.h. */
- void
- btrace_set_insn_history (struct btrace_thread_info *btinfo,
- const struct btrace_insn_iterator *begin,
- const struct btrace_insn_iterator *end)
- {
- if (btinfo->insn_history == NULL)
- btinfo->insn_history = XCNEW (struct btrace_insn_history);
- btinfo->insn_history->begin = *begin;
- btinfo->insn_history->end = *end;
- }
- /* See btrace.h. */
- void
- btrace_set_call_history (struct btrace_thread_info *btinfo,
- const struct btrace_call_iterator *begin,
- const struct btrace_call_iterator *end)
- {
- gdb_assert (begin->btinfo == end->btinfo);
- if (btinfo->call_history == NULL)
- btinfo->call_history = XCNEW (struct btrace_call_history);
- btinfo->call_history->begin = *begin;
- btinfo->call_history->end = *end;
- }
- /* See btrace.h. */
- int
- btrace_is_replaying (struct thread_info *tp)
- {
- return tp->btrace.replay != NULL;
- }
- /* See btrace.h. */
- int
- btrace_is_empty (struct thread_info *tp)
- {
- struct btrace_insn_iterator begin, end;
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- if (btinfo->functions.empty ())
- return 1;
- btrace_insn_begin (&begin, btinfo);
- btrace_insn_end (&end, btinfo);
- return btrace_insn_cmp (&begin, &end) == 0;
- }
- #if defined (HAVE_LIBIPT)
- /* Print a single packet. */
- static void
- pt_print_packet (const struct pt_packet *packet)
- {
- switch (packet->type)
- {
- default:
- gdb_printf (("[??: %x]"), packet->type);
- break;
- case ppt_psb:
- gdb_printf (("psb"));
- break;
- case ppt_psbend:
- gdb_printf (("psbend"));
- break;
- case ppt_pad:
- gdb_printf (("pad"));
- break;
- case ppt_tip:
- gdb_printf (("tip %u: 0x%" PRIx64 ""),
- packet->payload.ip.ipc,
- packet->payload.ip.ip);
- break;
- case ppt_tip_pge:
- gdb_printf (("tip.pge %u: 0x%" PRIx64 ""),
- packet->payload.ip.ipc,
- packet->payload.ip.ip);
- break;
- case ppt_tip_pgd:
- gdb_printf (("tip.pgd %u: 0x%" PRIx64 ""),
- packet->payload.ip.ipc,
- packet->payload.ip.ip);
- break;
- case ppt_fup:
- gdb_printf (("fup %u: 0x%" PRIx64 ""),
- packet->payload.ip.ipc,
- packet->payload.ip.ip);
- break;
- case ppt_tnt_8:
- gdb_printf (("tnt-8 %u: 0x%" PRIx64 ""),
- packet->payload.tnt.bit_size,
- packet->payload.tnt.payload);
- break;
- case ppt_tnt_64:
- gdb_printf (("tnt-64 %u: 0x%" PRIx64 ""),
- packet->payload.tnt.bit_size,
- packet->payload.tnt.payload);
- break;
- case ppt_pip:
- gdb_printf (("pip %" PRIx64 "%s"), packet->payload.pip.cr3,
- packet->payload.pip.nr ? (" nr") : (""));
- break;
- case ppt_tsc:
- gdb_printf (("tsc %" PRIx64 ""), packet->payload.tsc.tsc);
- break;
- case ppt_cbr:
- gdb_printf (("cbr %u"), packet->payload.cbr.ratio);
- break;
- case ppt_mode:
- switch (packet->payload.mode.leaf)
- {
- default:
- gdb_printf (("mode %u"), packet->payload.mode.leaf);
- break;
- case pt_mol_exec:
- gdb_printf (("mode.exec%s%s"),
- packet->payload.mode.bits.exec.csl
- ? (" cs.l") : (""),
- packet->payload.mode.bits.exec.csd
- ? (" cs.d") : (""));
- break;
- case pt_mol_tsx:
- gdb_printf (("mode.tsx%s%s"),
- packet->payload.mode.bits.tsx.intx
- ? (" intx") : (""),
- packet->payload.mode.bits.tsx.abrt
- ? (" abrt") : (""));
- break;
- }
- break;
- case ppt_ovf:
- gdb_printf (("ovf"));
- break;
- case ppt_stop:
- gdb_printf (("stop"));
- break;
- case ppt_vmcs:
- gdb_printf (("vmcs %" PRIx64 ""), packet->payload.vmcs.base);
- break;
- case ppt_tma:
- gdb_printf (("tma %x %x"), packet->payload.tma.ctc,
- packet->payload.tma.fc);
- break;
- case ppt_mtc:
- gdb_printf (("mtc %x"), packet->payload.mtc.ctc);
- break;
- case ppt_cyc:
- gdb_printf (("cyc %" PRIx64 ""), packet->payload.cyc.value);
- break;
- case ppt_mnt:
- gdb_printf (("mnt %" PRIx64 ""), packet->payload.mnt.payload);
- break;
- }
- }
- /* Decode packets into MAINT using DECODER. */
- static void
- btrace_maint_decode_pt (struct btrace_maint_info *maint,
- struct pt_packet_decoder *decoder)
- {
- int errcode;
- if (maint->variant.pt.packets == NULL)
- maint->variant.pt.packets = new std::vector<btrace_pt_packet>;
- for (;;)
- {
- struct btrace_pt_packet packet;
- errcode = pt_pkt_sync_forward (decoder);
- if (errcode < 0)
- break;
- for (;;)
- {
- pt_pkt_get_offset (decoder, &packet.offset);
- errcode = pt_pkt_next (decoder, &packet.packet,
- sizeof(packet.packet));
- if (errcode < 0)
- break;
- if (maint_btrace_pt_skip_pad == 0 || packet.packet.type != ppt_pad)
- {
- packet.errcode = pt_errcode (errcode);
- maint->variant.pt.packets->push_back (packet);
- }
- }
- if (errcode == -pte_eos)
- break;
- packet.errcode = pt_errcode (errcode);
- maint->variant.pt.packets->push_back (packet);
- warning (_("Error at trace offset 0x%" PRIx64 ": %s."),
- packet.offset, pt_errstr (packet.errcode));
- }
- if (errcode != -pte_eos)
- warning (_("Failed to synchronize onto the Intel Processor Trace "
- "stream: %s."), pt_errstr (pt_errcode (errcode)));
- }
- /* Update the packet history in BTINFO. */
- static void
- btrace_maint_update_pt_packets (struct btrace_thread_info *btinfo)
- {
- struct pt_packet_decoder *decoder;
- const struct btrace_cpu *cpu;
- struct btrace_data_pt *pt;
- struct pt_config config;
- int errcode;
- pt = &btinfo->data.variant.pt;
- /* Nothing to do if there is no trace. */
- if (pt->size == 0)
- return;
- memset (&config, 0, sizeof(config));
- config.size = sizeof (config);
- config.begin = pt->data;
- config.end = pt->data + pt->size;
- cpu = record_btrace_get_cpu ();
- if (cpu == nullptr)
- cpu = &pt->config.cpu;
- /* We treat an unknown vendor as 'no errata'. */
- if (cpu->vendor != CV_UNKNOWN)
- {
- config.cpu.vendor = pt_translate_cpu_vendor (cpu->vendor);
- config.cpu.family = cpu->family;
- config.cpu.model = cpu->model;
- config.cpu.stepping = cpu->stepping;
- errcode = pt_cpu_errata (&config.errata, &config.cpu);
- if (errcode < 0)
- error (_("Failed to configure the Intel Processor Trace "
- "decoder: %s."), pt_errstr (pt_errcode (errcode)));
- }
- decoder = pt_pkt_alloc_decoder (&config);
- if (decoder == NULL)
- error (_("Failed to allocate the Intel Processor Trace decoder."));
- try
- {
- btrace_maint_decode_pt (&btinfo->maint, decoder);
- }
- catch (const gdb_exception &except)
- {
- pt_pkt_free_decoder (decoder);
- if (except.reason < 0)
- throw;
- }
- pt_pkt_free_decoder (decoder);
- }
- #endif /* !defined (HAVE_LIBIPT) */
- /* Update the packet maintenance information for BTINFO and store the
- low and high bounds into BEGIN and END, respectively.
- Store the current iterator state into FROM and TO. */
- static void
- btrace_maint_update_packets (struct btrace_thread_info *btinfo,
- unsigned int *begin, unsigned int *end,
- unsigned int *from, unsigned int *to)
- {
- switch (btinfo->data.format)
- {
- default:
- *begin = 0;
- *end = 0;
- *from = 0;
- *to = 0;
- break;
- case BTRACE_FORMAT_BTS:
- /* Nothing to do - we operate directly on BTINFO->DATA. */
- *begin = 0;
- *end = btinfo->data.variant.bts.blocks->size ();
- *from = btinfo->maint.variant.bts.packet_history.begin;
- *to = btinfo->maint.variant.bts.packet_history.end;
- break;
- #if defined (HAVE_LIBIPT)
- case BTRACE_FORMAT_PT:
- if (btinfo->maint.variant.pt.packets == nullptr)
- btinfo->maint.variant.pt.packets = new std::vector<btrace_pt_packet>;
- if (btinfo->maint.variant.pt.packets->empty ())
- btrace_maint_update_pt_packets (btinfo);
- *begin = 0;
- *end = btinfo->maint.variant.pt.packets->size ();
- *from = btinfo->maint.variant.pt.packet_history.begin;
- *to = btinfo->maint.variant.pt.packet_history.end;
- break;
- #endif /* defined (HAVE_LIBIPT) */
- }
- }
- /* Print packets in BTINFO from BEGIN (inclusive) until END (exclusive) and
- update the current iterator position. */
- static void
- btrace_maint_print_packets (struct btrace_thread_info *btinfo,
- unsigned int begin, unsigned int end)
- {
- switch (btinfo->data.format)
- {
- default:
- break;
- case BTRACE_FORMAT_BTS:
- {
- const std::vector<btrace_block> &blocks
- = *btinfo->data.variant.bts.blocks;
- unsigned int blk;
- for (blk = begin; blk < end; ++blk)
- {
- const btrace_block &block = blocks.at (blk);
- gdb_printf ("%u\tbegin: %s, end: %s\n", blk,
- core_addr_to_string_nz (block.begin),
- core_addr_to_string_nz (block.end));
- }
- btinfo->maint.variant.bts.packet_history.begin = begin;
- btinfo->maint.variant.bts.packet_history.end = end;
- }
- break;
- #if defined (HAVE_LIBIPT)
- case BTRACE_FORMAT_PT:
- {
- const std::vector<btrace_pt_packet> &packets
- = *btinfo->maint.variant.pt.packets;
- unsigned int pkt;
- for (pkt = begin; pkt < end; ++pkt)
- {
- const struct btrace_pt_packet &packet = packets.at (pkt);
- gdb_printf ("%u\t", pkt);
- gdb_printf ("0x%" PRIx64 "\t", packet.offset);
- if (packet.errcode == pte_ok)
- pt_print_packet (&packet.packet);
- else
- gdb_printf ("[error: %s]", pt_errstr (packet.errcode));
- gdb_printf ("\n");
- }
- btinfo->maint.variant.pt.packet_history.begin = begin;
- btinfo->maint.variant.pt.packet_history.end = end;
- }
- break;
- #endif /* defined (HAVE_LIBIPT) */
- }
- }
- /* Read a number from an argument string. */
- static unsigned int
- get_uint (const char **arg)
- {
- const char *begin, *pos;
- char *end;
- unsigned long number;
- begin = *arg;
- pos = skip_spaces (begin);
- if (!isdigit (*pos))
- error (_("Expected positive number, got: %s."), pos);
- number = strtoul (pos, &end, 10);
- if (number > UINT_MAX)
- error (_("Number too big."));
- *arg += (end - begin);
- return (unsigned int) number;
- }
- /* Read a context size from an argument string. */
- static int
- get_context_size (const char **arg)
- {
- const char *pos = skip_spaces (*arg);
- if (!isdigit (*pos))
- error (_("Expected positive number, got: %s."), pos);
- char *end;
- long result = strtol (pos, &end, 10);
- *arg = end;
- return result;
- }
- /* Complain about junk at the end of an argument string. */
- static void
- no_chunk (const char *arg)
- {
- if (*arg != 0)
- error (_("Junk after argument: %s."), arg);
- }
- /* The "maintenance btrace packet-history" command. */
- static void
- maint_btrace_packet_history_cmd (const char *arg, int from_tty)
- {
- struct btrace_thread_info *btinfo;
- unsigned int size, begin, end, from, to;
- thread_info *tp = find_thread_ptid (current_inferior (), inferior_ptid);
- if (tp == NULL)
- error (_("No thread."));
- size = 10;
- btinfo = &tp->btrace;
- btrace_maint_update_packets (btinfo, &begin, &end, &from, &to);
- if (begin == end)
- {
- gdb_printf (_("No trace.\n"));
- return;
- }
- if (arg == NULL || *arg == 0 || strcmp (arg, "+") == 0)
- {
- from = to;
- if (end - from < size)
- size = end - from;
- to = from + size;
- }
- else if (strcmp (arg, "-") == 0)
- {
- to = from;
- if (to - begin < size)
- size = to - begin;
- from = to - size;
- }
- else
- {
- from = get_uint (&arg);
- if (end <= from)
- error (_("'%u' is out of range."), from);
- arg = skip_spaces (arg);
- if (*arg == ',')
- {
- arg = skip_spaces (++arg);
- if (*arg == '+')
- {
- arg += 1;
- size = get_context_size (&arg);
- no_chunk (arg);
- if (end - from < size)
- size = end - from;
- to = from + size;
- }
- else if (*arg == '-')
- {
- arg += 1;
- size = get_context_size (&arg);
- no_chunk (arg);
- /* Include the packet given as first argument. */
- from += 1;
- to = from;
- if (to - begin < size)
- size = to - begin;
- from = to - size;
- }
- else
- {
- to = get_uint (&arg);
- /* Include the packet at the second argument and silently
- truncate the range. */
- if (to < end)
- to += 1;
- else
- to = end;
- no_chunk (arg);
- }
- }
- else
- {
- no_chunk (arg);
- if (end - from < size)
- size = end - from;
- to = from + size;
- }
- dont_repeat ();
- }
- btrace_maint_print_packets (btinfo, from, to);
- }
- /* The "maintenance btrace clear-packet-history" command. */
- static void
- maint_btrace_clear_packet_history_cmd (const char *args, int from_tty)
- {
- if (args != NULL && *args != 0)
- error (_("Invalid argument."));
- if (inferior_ptid == null_ptid)
- error (_("No thread."));
- thread_info *tp = inferior_thread ();
- btrace_thread_info *btinfo = &tp->btrace;
- /* Must clear the maint data before - it depends on BTINFO->DATA. */
- btrace_maint_clear (btinfo);
- btinfo->data.clear ();
- }
- /* The "maintenance btrace clear" command. */
- static void
- maint_btrace_clear_cmd (const char *args, int from_tty)
- {
- if (args != NULL && *args != 0)
- error (_("Invalid argument."));
- if (inferior_ptid == null_ptid)
- error (_("No thread."));
- thread_info *tp = inferior_thread ();
- btrace_clear (tp);
- }
- /* The "maintenance info btrace" command. */
- static void
- maint_info_btrace_cmd (const char *args, int from_tty)
- {
- struct btrace_thread_info *btinfo;
- const struct btrace_config *conf;
- if (args != NULL && *args != 0)
- error (_("Invalid argument."));
- if (inferior_ptid == null_ptid)
- error (_("No thread."));
- thread_info *tp = inferior_thread ();
- btinfo = &tp->btrace;
- conf = btrace_conf (btinfo);
- if (conf == NULL)
- error (_("No btrace configuration."));
- gdb_printf (_("Format: %s.\n"),
- btrace_format_string (conf->format));
- switch (conf->format)
- {
- default:
- break;
- case BTRACE_FORMAT_BTS:
- gdb_printf (_("Number of packets: %zu.\n"),
- btinfo->data.variant.bts.blocks->size ());
- break;
- #if defined (HAVE_LIBIPT)
- case BTRACE_FORMAT_PT:
- {
- struct pt_version version;
- version = pt_library_version ();
- gdb_printf (_("Version: %u.%u.%u%s.\n"), version.major,
- version.minor, version.build,
- version.ext != NULL ? version.ext : "");
- btrace_maint_update_pt_packets (btinfo);
- gdb_printf (_("Number of packets: %zu.\n"),
- ((btinfo->maint.variant.pt.packets == nullptr)
- ? 0 : btinfo->maint.variant.pt.packets->size ()));
- }
- break;
- #endif /* defined (HAVE_LIBIPT) */
- }
- }
- /* The "maint show btrace pt skip-pad" show value function. */
- static void
- show_maint_btrace_pt_skip_pad (struct ui_file *file, int from_tty,
- struct cmd_list_element *c,
- const char *value)
- {
- gdb_printf (file, _("Skip PAD packets is %s.\n"), value);
- }
- /* Initialize btrace maintenance commands. */
- void _initialize_btrace ();
- void
- _initialize_btrace ()
- {
- add_cmd ("btrace", class_maintenance, maint_info_btrace_cmd,
- _("Info about branch tracing data."), &maintenanceinfolist);
- add_basic_prefix_cmd ("btrace", class_maintenance,
- _("Branch tracing maintenance commands."),
- &maint_btrace_cmdlist, 0, &maintenancelist);
- add_setshow_prefix_cmd ("btrace", class_maintenance,
- _("Set branch tracing specific variables."),
- _("Show branch tracing specific variables."),
- &maint_btrace_set_cmdlist,
- &maint_btrace_show_cmdlist,
- &maintenance_set_cmdlist,
- &maintenance_show_cmdlist);
- add_setshow_prefix_cmd ("pt", class_maintenance,
- _("Set Intel Processor Trace specific variables."),
- _("Show Intel Processor Trace specific variables."),
- &maint_btrace_pt_set_cmdlist,
- &maint_btrace_pt_show_cmdlist,
- &maint_btrace_set_cmdlist,
- &maint_btrace_show_cmdlist);
- add_setshow_boolean_cmd ("skip-pad", class_maintenance,
- &maint_btrace_pt_skip_pad, _("\
- Set whether PAD packets should be skipped in the btrace packet history."), _("\
- Show whether PAD packets should be skipped in the btrace packet history."),_("\
- When enabled, PAD packets are ignored in the btrace packet history."),
- NULL, show_maint_btrace_pt_skip_pad,
- &maint_btrace_pt_set_cmdlist,
- &maint_btrace_pt_show_cmdlist);
- add_cmd ("packet-history", class_maintenance, maint_btrace_packet_history_cmd,
- _("Print the raw branch tracing data.\n\
- With no argument, print ten more packets after the previous ten-line print.\n\
- With '-' as argument print ten packets before a previous ten-line print.\n\
- One argument specifies the starting packet of a ten-line print.\n\
- Two arguments with comma between specify starting and ending packets to \
- print.\n\
- Preceded with '+'/'-' the second argument specifies the distance from the \
- first."),
- &maint_btrace_cmdlist);
- add_cmd ("clear-packet-history", class_maintenance,
- maint_btrace_clear_packet_history_cmd,
- _("Clears the branch tracing packet history.\n\
- Discards the raw branch tracing data but not the execution history data."),
- &maint_btrace_cmdlist);
- add_cmd ("clear", class_maintenance, maint_btrace_clear_cmd,
- _("Clears the branch tracing data.\n\
- Discards the raw branch tracing data and the execution history data.\n\
- The next 'record' command will fetch the branch tracing data anew."),
- &maint_btrace_cmdlist);
- }
|