12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215 |
- /* 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 "record.h"
- #include "record-btrace.h"
- #include "gdbthread.h"
- #include "target.h"
- #include "gdbcmd.h"
- #include "disasm.h"
- #include "observable.h"
- #include "cli/cli-utils.h"
- #include "source.h"
- #include "ui-out.h"
- #include "symtab.h"
- #include "filenames.h"
- #include "regcache.h"
- #include "frame-unwind.h"
- #include "hashtab.h"
- #include "infrun.h"
- #include "gdbsupport/event-loop.h"
- #include "inf-loop.h"
- #include "inferior.h"
- #include <algorithm>
- #include "gdbarch.h"
- #include "cli/cli-style.h"
- #include "async-event.h"
- #include <forward_list>
- static const target_info record_btrace_target_info = {
- "record-btrace",
- N_("Branch tracing target"),
- N_("Collect control-flow trace and provide the execution history.")
- };
- /* The target_ops of record-btrace. */
- class record_btrace_target final : public target_ops
- {
- public:
- const target_info &info () const override
- { return record_btrace_target_info; }
- strata stratum () const override { return record_stratum; }
- void close () override;
- void async (int) override;
- void detach (inferior *inf, int from_tty) override
- { record_detach (this, inf, from_tty); }
- void disconnect (const char *, int) override;
- void mourn_inferior () override
- { record_mourn_inferior (this); }
- void kill () override
- { record_kill (this); }
- enum record_method record_method (ptid_t ptid) override;
- void stop_recording () override;
- void info_record () override;
- void insn_history (int size, gdb_disassembly_flags flags) override;
- void insn_history_from (ULONGEST from, int size,
- gdb_disassembly_flags flags) override;
- void insn_history_range (ULONGEST begin, ULONGEST end,
- gdb_disassembly_flags flags) override;
- void call_history (int size, record_print_flags flags) override;
- void call_history_from (ULONGEST begin, int size, record_print_flags flags)
- override;
- void call_history_range (ULONGEST begin, ULONGEST end, record_print_flags flags)
- override;
- bool record_is_replaying (ptid_t ptid) override;
- bool record_will_replay (ptid_t ptid, int dir) override;
- void record_stop_replaying () override;
- enum target_xfer_status xfer_partial (enum target_object object,
- const char *annex,
- gdb_byte *readbuf,
- const gdb_byte *writebuf,
- ULONGEST offset, ULONGEST len,
- ULONGEST *xfered_len) override;
- int insert_breakpoint (struct gdbarch *,
- struct bp_target_info *) override;
- int remove_breakpoint (struct gdbarch *, struct bp_target_info *,
- enum remove_bp_reason) override;
- void fetch_registers (struct regcache *, int) override;
- void store_registers (struct regcache *, int) override;
- void prepare_to_store (struct regcache *) override;
- const struct frame_unwind *get_unwinder () override;
- const struct frame_unwind *get_tailcall_unwinder () override;
- void resume (ptid_t, int, enum gdb_signal) override;
- ptid_t wait (ptid_t, struct target_waitstatus *, target_wait_flags) override;
- void stop (ptid_t) override;
- void update_thread_list () override;
- bool thread_alive (ptid_t ptid) override;
- void goto_record_begin () override;
- void goto_record_end () override;
- void goto_record (ULONGEST insn) override;
- bool can_execute_reverse () override;
- bool stopped_by_sw_breakpoint () override;
- bool supports_stopped_by_sw_breakpoint () override;
- bool stopped_by_hw_breakpoint () override;
- bool supports_stopped_by_hw_breakpoint () override;
- enum exec_direction_kind execution_direction () override;
- void prepare_to_generate_core () override;
- void done_generating_core () override;
- };
- static record_btrace_target record_btrace_ops;
- /* Initialize the record-btrace target ops. */
- /* Token associated with a new-thread observer enabling branch tracing
- for the new thread. */
- static const gdb::observers::token record_btrace_thread_observer_token {};
- /* Memory access types used in set/show record btrace replay-memory-access. */
- static const char replay_memory_access_read_only[] = "read-only";
- static const char replay_memory_access_read_write[] = "read-write";
- static const char *const replay_memory_access_types[] =
- {
- replay_memory_access_read_only,
- replay_memory_access_read_write,
- NULL
- };
- /* The currently allowed replay memory access type. */
- static const char *replay_memory_access = replay_memory_access_read_only;
- /* The cpu state kinds. */
- enum record_btrace_cpu_state_kind
- {
- CS_AUTO,
- CS_NONE,
- CS_CPU
- };
- /* The current cpu state. */
- static enum record_btrace_cpu_state_kind record_btrace_cpu_state = CS_AUTO;
- /* The current cpu for trace decode. */
- static struct btrace_cpu record_btrace_cpu;
- /* Command lists for "set/show record btrace". */
- static struct cmd_list_element *set_record_btrace_cmdlist;
- static struct cmd_list_element *show_record_btrace_cmdlist;
- /* The execution direction of the last resume we got. See record-full.c. */
- static enum exec_direction_kind record_btrace_resume_exec_dir = EXEC_FORWARD;
- /* The async event handler for reverse/replay execution. */
- static struct async_event_handler *record_btrace_async_inferior_event_handler;
- /* A flag indicating that we are currently generating a core file. */
- static int record_btrace_generating_corefile;
- /* The current branch trace configuration. */
- static struct btrace_config record_btrace_conf;
- /* Command list for "record btrace". */
- static struct cmd_list_element *record_btrace_cmdlist;
- /* Command lists for "set/show record btrace bts". */
- static struct cmd_list_element *set_record_btrace_bts_cmdlist;
- static struct cmd_list_element *show_record_btrace_bts_cmdlist;
- /* Command lists for "set/show record btrace pt". */
- static struct cmd_list_element *set_record_btrace_pt_cmdlist;
- static struct cmd_list_element *show_record_btrace_pt_cmdlist;
- /* Command list for "set record btrace cpu". */
- static struct cmd_list_element *set_record_btrace_cpu_cmdlist;
- /* Print a record-btrace 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, \
- "[record-btrace] " msg "\n", ##args); \
- } \
- while (0)
- /* Return the cpu configured by the user. Returns NULL if the cpu was
- configured as auto. */
- const struct btrace_cpu *
- record_btrace_get_cpu (void)
- {
- switch (record_btrace_cpu_state)
- {
- case CS_AUTO:
- return nullptr;
- case CS_NONE:
- record_btrace_cpu.vendor = CV_UNKNOWN;
- /* Fall through. */
- case CS_CPU:
- return &record_btrace_cpu;
- }
- error (_("Internal error: bad record btrace cpu state."));
- }
- /* Update the branch trace for the current thread and return a pointer to its
- thread_info.
- Throws an error if there is no thread or no trace. This function never
- returns NULL. */
- static struct thread_info *
- require_btrace_thread (void)
- {
- DEBUG ("require");
- if (inferior_ptid == null_ptid)
- error (_("No thread."));
- thread_info *tp = inferior_thread ();
- validate_registers_access ();
- btrace_fetch (tp, record_btrace_get_cpu ());
- if (btrace_is_empty (tp))
- error (_("No trace."));
- return tp;
- }
- /* Update the branch trace for the current thread and return a pointer to its
- branch trace information struct.
- Throws an error if there is no thread or no trace. This function never
- returns NULL. */
- static struct btrace_thread_info *
- require_btrace (void)
- {
- struct thread_info *tp;
- tp = require_btrace_thread ();
- return &tp->btrace;
- }
- /* The new thread observer. */
- static void
- record_btrace_on_new_thread (struct thread_info *tp)
- {
- /* Ignore this thread if its inferior is not recorded by us. */
- target_ops *rec = tp->inf->target_at (record_stratum);
- if (rec != &record_btrace_ops)
- return;
- try
- {
- btrace_enable (tp, &record_btrace_conf);
- }
- catch (const gdb_exception_error &error)
- {
- warning ("%s", error.what ());
- }
- }
- /* Enable automatic tracing of new threads. */
- static void
- record_btrace_auto_enable (void)
- {
- DEBUG ("attach thread observer");
- gdb::observers::new_thread.attach (record_btrace_on_new_thread,
- record_btrace_thread_observer_token,
- "record-btrace");
- }
- /* Disable automatic tracing of new threads. */
- static void
- record_btrace_auto_disable (void)
- {
- DEBUG ("detach thread observer");
- gdb::observers::new_thread.detach (record_btrace_thread_observer_token);
- }
- /* The record-btrace async event handler function. */
- static void
- record_btrace_handle_async_inferior_event (gdb_client_data data)
- {
- inferior_event_handler (INF_REG_EVENT);
- }
- /* See record-btrace.h. */
- void
- record_btrace_push_target (void)
- {
- const char *format;
- record_btrace_auto_enable ();
- current_inferior ()->push_target (&record_btrace_ops);
- record_btrace_async_inferior_event_handler
- = create_async_event_handler (record_btrace_handle_async_inferior_event,
- NULL, "record-btrace");
- record_btrace_generating_corefile = 0;
- format = btrace_format_short_string (record_btrace_conf.format);
- gdb::observers::record_changed.notify (current_inferior (), 1, "btrace", format);
- }
- /* Disable btrace on a set of threads on scope exit. */
- struct scoped_btrace_disable
- {
- scoped_btrace_disable () = default;
- DISABLE_COPY_AND_ASSIGN (scoped_btrace_disable);
- ~scoped_btrace_disable ()
- {
- for (thread_info *tp : m_threads)
- btrace_disable (tp);
- }
- void add_thread (thread_info *thread)
- {
- m_threads.push_front (thread);
- }
- void discard ()
- {
- m_threads.clear ();
- }
- private:
- std::forward_list<thread_info *> m_threads;
- };
- /* Open target record-btrace. */
- static void
- record_btrace_target_open (const char *args, int from_tty)
- {
- /* If we fail to enable btrace for one thread, disable it for the threads for
- which it was successfully enabled. */
- scoped_btrace_disable btrace_disable;
- DEBUG ("open");
- record_preopen ();
- if (!target_has_execution ())
- error (_("The program is not being run."));
- for (thread_info *tp : current_inferior ()->non_exited_threads ())
- if (args == NULL || *args == 0 || number_is_in_list (args, tp->global_num))
- {
- btrace_enable (tp, &record_btrace_conf);
- btrace_disable.add_thread (tp);
- }
- record_btrace_push_target ();
- btrace_disable.discard ();
- }
- /* The stop_recording method of target record-btrace. */
- void
- record_btrace_target::stop_recording ()
- {
- DEBUG ("stop recording");
- record_btrace_auto_disable ();
- for (thread_info *tp : current_inferior ()->non_exited_threads ())
- if (tp->btrace.target != NULL)
- btrace_disable (tp);
- }
- /* The disconnect method of target record-btrace. */
- void
- record_btrace_target::disconnect (const char *args,
- int from_tty)
- {
- struct target_ops *beneath = this->beneath ();
- /* Do not stop recording, just clean up GDB side. */
- current_inferior ()->unpush_target (this);
- /* Forward disconnect. */
- beneath->disconnect (args, from_tty);
- }
- /* The close method of target record-btrace. */
- void
- record_btrace_target::close ()
- {
- if (record_btrace_async_inferior_event_handler != NULL)
- delete_async_event_handler (&record_btrace_async_inferior_event_handler);
- /* Make sure automatic recording gets disabled even if we did not stop
- recording before closing the record-btrace target. */
- record_btrace_auto_disable ();
- /* We should have already stopped recording.
- Tear down btrace in case we have not. */
- for (thread_info *tp : current_inferior ()->non_exited_threads ())
- btrace_teardown (tp);
- }
- /* The async method of target record-btrace. */
- void
- record_btrace_target::async (int enable)
- {
- if (enable)
- mark_async_event_handler (record_btrace_async_inferior_event_handler);
- else
- clear_async_event_handler (record_btrace_async_inferior_event_handler);
- this->beneath ()->async (enable);
- }
- /* Adjusts the size and returns a human readable size suffix. */
- static const char *
- record_btrace_adjust_size (unsigned int *size)
- {
- unsigned int sz;
- sz = *size;
- if ((sz & ((1u << 30) - 1)) == 0)
- {
- *size = sz >> 30;
- return "GB";
- }
- else if ((sz & ((1u << 20) - 1)) == 0)
- {
- *size = sz >> 20;
- return "MB";
- }
- else if ((sz & ((1u << 10) - 1)) == 0)
- {
- *size = sz >> 10;
- return "kB";
- }
- else
- return "";
- }
- /* Print a BTS configuration. */
- static void
- record_btrace_print_bts_conf (const struct btrace_config_bts *conf)
- {
- const char *suffix;
- unsigned int size;
- size = conf->size;
- if (size > 0)
- {
- suffix = record_btrace_adjust_size (&size);
- gdb_printf (_("Buffer size: %u%s.\n"), size, suffix);
- }
- }
- /* Print an Intel Processor Trace configuration. */
- static void
- record_btrace_print_pt_conf (const struct btrace_config_pt *conf)
- {
- const char *suffix;
- unsigned int size;
- size = conf->size;
- if (size > 0)
- {
- suffix = record_btrace_adjust_size (&size);
- gdb_printf (_("Buffer size: %u%s.\n"), size, suffix);
- }
- }
- /* Print a branch tracing configuration. */
- static void
- record_btrace_print_conf (const struct btrace_config *conf)
- {
- gdb_printf (_("Recording format: %s.\n"),
- btrace_format_string (conf->format));
- switch (conf->format)
- {
- case BTRACE_FORMAT_NONE:
- return;
- case BTRACE_FORMAT_BTS:
- record_btrace_print_bts_conf (&conf->bts);
- return;
- case BTRACE_FORMAT_PT:
- record_btrace_print_pt_conf (&conf->pt);
- return;
- }
- internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
- }
- /* The info_record method of target record-btrace. */
- void
- record_btrace_target::info_record ()
- {
- struct btrace_thread_info *btinfo;
- const struct btrace_config *conf;
- struct thread_info *tp;
- unsigned int insns, calls, gaps;
- DEBUG ("info");
- if (inferior_ptid == null_ptid)
- error (_("No thread."));
- tp = inferior_thread ();
- validate_registers_access ();
- btinfo = &tp->btrace;
- conf = ::btrace_conf (btinfo);
- if (conf != NULL)
- record_btrace_print_conf (conf);
- btrace_fetch (tp, record_btrace_get_cpu ());
- insns = 0;
- calls = 0;
- gaps = 0;
- if (!btrace_is_empty (tp))
- {
- struct btrace_call_iterator call;
- struct btrace_insn_iterator insn;
- btrace_call_end (&call, btinfo);
- btrace_call_prev (&call, 1);
- calls = btrace_call_number (&call);
- btrace_insn_end (&insn, btinfo);
- insns = btrace_insn_number (&insn);
- /* If the last instruction is not a gap, it is the current instruction
- that is not actually part of the record. */
- if (btrace_insn_get (&insn) != NULL)
- insns -= 1;
- gaps = btinfo->ngaps;
- }
- gdb_printf (_("Recorded %u instructions in %u functions (%u gaps) "
- "for thread %s (%s).\n"), insns, calls, gaps,
- print_thread_id (tp),
- target_pid_to_str (tp->ptid).c_str ());
- if (btrace_is_replaying (tp))
- gdb_printf (_("Replay in progress. At instruction %u.\n"),
- btrace_insn_number (btinfo->replay));
- }
- /* Print a decode error. */
- static void
- btrace_ui_out_decode_error (struct ui_out *uiout, int errcode,
- enum btrace_format format)
- {
- const char *errstr = btrace_decode_error (format, errcode);
- uiout->text (_("["));
- /* ERRCODE > 0 indicates notifications on BTRACE_FORMAT_PT. */
- if (!(format == BTRACE_FORMAT_PT && errcode > 0))
- {
- uiout->text (_("decode error ("));
- uiout->field_signed ("errcode", errcode);
- uiout->text (_("): "));
- }
- uiout->text (errstr);
- uiout->text (_("]\n"));
- }
- /* A range of source lines. */
- struct btrace_line_range
- {
- /* The symtab this line is from. */
- struct symtab *symtab;
- /* The first line (inclusive). */
- int begin;
- /* The last line (exclusive). */
- int end;
- };
- /* Construct a line range. */
- static struct btrace_line_range
- btrace_mk_line_range (struct symtab *symtab, int begin, int end)
- {
- struct btrace_line_range range;
- range.symtab = symtab;
- range.begin = begin;
- range.end = end;
- return range;
- }
- /* Add a line to a line range. */
- static struct btrace_line_range
- btrace_line_range_add (struct btrace_line_range range, int line)
- {
- if (range.end <= range.begin)
- {
- /* This is the first entry. */
- range.begin = line;
- range.end = line + 1;
- }
- else if (line < range.begin)
- range.begin = line;
- else if (range.end < line)
- range.end = line;
- return range;
- }
- /* Return non-zero if RANGE is empty, zero otherwise. */
- static int
- btrace_line_range_is_empty (struct btrace_line_range range)
- {
- return range.end <= range.begin;
- }
- /* Return non-zero if LHS contains RHS, zero otherwise. */
- static int
- btrace_line_range_contains_range (struct btrace_line_range lhs,
- struct btrace_line_range rhs)
- {
- return ((lhs.symtab == rhs.symtab)
- && (lhs.begin <= rhs.begin)
- && (rhs.end <= lhs.end));
- }
- /* Find the line range associated with PC. */
- static struct btrace_line_range
- btrace_find_line_range (CORE_ADDR pc)
- {
- struct btrace_line_range range;
- struct linetable_entry *lines;
- struct linetable *ltable;
- struct symtab *symtab;
- int nlines, i;
- symtab = find_pc_line_symtab (pc);
- if (symtab == NULL)
- return btrace_mk_line_range (NULL, 0, 0);
- ltable = symtab->linetable ();
- if (ltable == NULL)
- return btrace_mk_line_range (symtab, 0, 0);
- nlines = ltable->nitems;
- lines = ltable->item;
- if (nlines <= 0)
- return btrace_mk_line_range (symtab, 0, 0);
- range = btrace_mk_line_range (symtab, 0, 0);
- for (i = 0; i < nlines - 1; i++)
- {
- /* The test of is_stmt here was added when the is_stmt field was
- introduced to the 'struct linetable_entry' structure. This
- ensured that this loop maintained the same behaviour as before we
- introduced is_stmt. That said, it might be that we would be
- better off not checking is_stmt here, this would lead to us
- possibly adding more line numbers to the range. At the time this
- change was made I was unsure how to test this so chose to go with
- maintaining the existing experience. */
- if ((lines[i].pc == pc) && (lines[i].line != 0)
- && (lines[i].is_stmt == 1))
- range = btrace_line_range_add (range, lines[i].line);
- }
- return range;
- }
- /* Print source lines in LINES to UIOUT.
- UI_ITEM_CHAIN is a cleanup chain for the last source line and the
- instructions corresponding to that source line. When printing a new source
- line, we do the cleanups for the open chain and open a new cleanup chain for
- the new source line. If the source line range in LINES is not empty, this
- function will leave the cleanup chain for the last printed source line open
- so instructions can be added to it. */
- static void
- btrace_print_lines (struct btrace_line_range lines, struct ui_out *uiout,
- gdb::optional<ui_out_emit_tuple> *src_and_asm_tuple,
- gdb::optional<ui_out_emit_list> *asm_list,
- gdb_disassembly_flags flags)
- {
- print_source_lines_flags psl_flags;
- if (flags & DISASSEMBLY_FILENAME)
- psl_flags |= PRINT_SOURCE_LINES_FILENAME;
- for (int line = lines.begin; line < lines.end; ++line)
- {
- asm_list->reset ();
- src_and_asm_tuple->emplace (uiout, "src_and_asm_line");
- print_source_lines (lines.symtab, line, line + 1, psl_flags);
- asm_list->emplace (uiout, "line_asm_insn");
- }
- }
- /* Disassemble a section of the recorded instruction trace. */
- static void
- btrace_insn_history (struct ui_out *uiout,
- const struct btrace_thread_info *btinfo,
- const struct btrace_insn_iterator *begin,
- const struct btrace_insn_iterator *end,
- gdb_disassembly_flags flags)
- {
- DEBUG ("itrace (0x%x): [%u; %u)", (unsigned) flags,
- btrace_insn_number (begin), btrace_insn_number (end));
- flags |= DISASSEMBLY_SPECULATIVE;
- struct gdbarch *gdbarch = target_gdbarch ();
- btrace_line_range last_lines = btrace_mk_line_range (NULL, 0, 0);
- ui_out_emit_list list_emitter (uiout, "asm_insns");
- gdb::optional<ui_out_emit_tuple> src_and_asm_tuple;
- gdb::optional<ui_out_emit_list> asm_list;
- gdb_pretty_print_disassembler disasm (gdbarch, uiout);
- for (btrace_insn_iterator it = *begin; btrace_insn_cmp (&it, end) != 0;
- btrace_insn_next (&it, 1))
- {
- const struct btrace_insn *insn;
- insn = btrace_insn_get (&it);
- /* A NULL instruction indicates a gap in the trace. */
- if (insn == NULL)
- {
- const struct btrace_config *conf;
- conf = btrace_conf (btinfo);
- /* We have trace so we must have a configuration. */
- gdb_assert (conf != NULL);
- uiout->field_fmt ("insn-number", "%u",
- btrace_insn_number (&it));
- uiout->text ("\t");
- btrace_ui_out_decode_error (uiout, btrace_insn_get_error (&it),
- conf->format);
- }
- else
- {
- struct disasm_insn dinsn;
- if ((flags & DISASSEMBLY_SOURCE) != 0)
- {
- struct btrace_line_range lines;
- lines = btrace_find_line_range (insn->pc);
- if (!btrace_line_range_is_empty (lines)
- && !btrace_line_range_contains_range (last_lines, lines))
- {
- btrace_print_lines (lines, uiout, &src_and_asm_tuple, &asm_list,
- flags);
- last_lines = lines;
- }
- else if (!src_and_asm_tuple.has_value ())
- {
- gdb_assert (!asm_list.has_value ());
- src_and_asm_tuple.emplace (uiout, "src_and_asm_line");
- /* No source information. */
- asm_list.emplace (uiout, "line_asm_insn");
- }
- gdb_assert (src_and_asm_tuple.has_value ());
- gdb_assert (asm_list.has_value ());
- }
- memset (&dinsn, 0, sizeof (dinsn));
- dinsn.number = btrace_insn_number (&it);
- dinsn.addr = insn->pc;
- if ((insn->flags & BTRACE_INSN_FLAG_SPECULATIVE) != 0)
- dinsn.is_speculative = 1;
- disasm.pretty_print_insn (&dinsn, flags);
- }
- }
- }
- /* The insn_history method of target record-btrace. */
- void
- record_btrace_target::insn_history (int size, gdb_disassembly_flags flags)
- {
- struct btrace_thread_info *btinfo;
- struct btrace_insn_history *history;
- struct btrace_insn_iterator begin, end;
- struct ui_out *uiout;
- unsigned int context, covered;
- uiout = current_uiout;
- ui_out_emit_tuple tuple_emitter (uiout, "insn history");
- context = abs (size);
- if (context == 0)
- error (_("Bad record instruction-history-size."));
- btinfo = require_btrace ();
- history = btinfo->insn_history;
- if (history == NULL)
- {
- struct btrace_insn_iterator *replay;
- DEBUG ("insn-history (0x%x): %d", (unsigned) flags, size);
- /* If we're replaying, we start at the replay position. Otherwise, we
- start at the tail of the trace. */
- replay = btinfo->replay;
- if (replay != NULL)
- begin = *replay;
- else
- btrace_insn_end (&begin, btinfo);
- /* We start from here and expand in the requested direction. Then we
- expand in the other direction, as well, to fill up any remaining
- context. */
- end = begin;
- if (size < 0)
- {
- /* We want the current position covered, as well. */
- covered = btrace_insn_next (&end, 1);
- covered += btrace_insn_prev (&begin, context - covered);
- covered += btrace_insn_next (&end, context - covered);
- }
- else
- {
- covered = btrace_insn_next (&end, context);
- covered += btrace_insn_prev (&begin, context - covered);
- }
- }
- else
- {
- begin = history->begin;
- end = history->end;
- DEBUG ("insn-history (0x%x): %d, prev: [%u; %u)", (unsigned) flags, size,
- btrace_insn_number (&begin), btrace_insn_number (&end));
- if (size < 0)
- {
- end = begin;
- covered = btrace_insn_prev (&begin, context);
- }
- else
- {
- begin = end;
- covered = btrace_insn_next (&end, context);
- }
- }
- if (covered > 0)
- btrace_insn_history (uiout, btinfo, &begin, &end, flags);
- else
- {
- if (size < 0)
- gdb_printf (_("At the start of the branch trace record.\n"));
- else
- gdb_printf (_("At the end of the branch trace record.\n"));
- }
- btrace_set_insn_history (btinfo, &begin, &end);
- }
- /* The insn_history_range method of target record-btrace. */
- void
- record_btrace_target::insn_history_range (ULONGEST from, ULONGEST to,
- gdb_disassembly_flags flags)
- {
- struct btrace_thread_info *btinfo;
- struct btrace_insn_iterator begin, end;
- struct ui_out *uiout;
- unsigned int low, high;
- int found;
- uiout = current_uiout;
- ui_out_emit_tuple tuple_emitter (uiout, "insn history");
- low = from;
- high = to;
- DEBUG ("insn-history (0x%x): [%u; %u)", (unsigned) flags, low, high);
- /* Check for wrap-arounds. */
- if (low != from || high != to)
- error (_("Bad range."));
- if (high < low)
- error (_("Bad range."));
- btinfo = require_btrace ();
- found = btrace_find_insn_by_number (&begin, btinfo, low);
- if (found == 0)
- error (_("Range out of bounds."));
- found = btrace_find_insn_by_number (&end, btinfo, high);
- if (found == 0)
- {
- /* Silently truncate the range. */
- btrace_insn_end (&end, btinfo);
- }
- else
- {
- /* We want both begin and end to be inclusive. */
- btrace_insn_next (&end, 1);
- }
- btrace_insn_history (uiout, btinfo, &begin, &end, flags);
- btrace_set_insn_history (btinfo, &begin, &end);
- }
- /* The insn_history_from method of target record-btrace. */
- void
- record_btrace_target::insn_history_from (ULONGEST from, int size,
- gdb_disassembly_flags flags)
- {
- ULONGEST begin, end, context;
- context = abs (size);
- if (context == 0)
- error (_("Bad record instruction-history-size."));
- if (size < 0)
- {
- end = from;
- if (from < context)
- begin = 0;
- else
- begin = from - context + 1;
- }
- else
- {
- begin = from;
- end = from + context - 1;
- /* Check for wrap-around. */
- if (end < begin)
- end = ULONGEST_MAX;
- }
- insn_history_range (begin, end, flags);
- }
- /* Print the instruction number range for a function call history line. */
- static void
- btrace_call_history_insn_range (struct ui_out *uiout,
- const struct btrace_function *bfun)
- {
- unsigned int begin, end, size;
- size = bfun->insn.size ();
- gdb_assert (size > 0);
- begin = bfun->insn_offset;
- end = begin + size - 1;
- uiout->field_unsigned ("insn begin", begin);
- uiout->text (",");
- uiout->field_unsigned ("insn end", end);
- }
- /* Compute the lowest and highest source line for the instructions in BFUN
- and return them in PBEGIN and PEND.
- Ignore instructions that can't be mapped to BFUN, e.g. instructions that
- result from inlining or macro expansion. */
- static void
- btrace_compute_src_line_range (const struct btrace_function *bfun,
- int *pbegin, int *pend)
- {
- struct symtab *symtab;
- struct symbol *sym;
- int begin, end;
- begin = INT_MAX;
- end = INT_MIN;
- sym = bfun->sym;
- if (sym == NULL)
- goto out;
- symtab = symbol_symtab (sym);
- for (const btrace_insn &insn : bfun->insn)
- {
- struct symtab_and_line sal;
- sal = find_pc_line (insn.pc, 0);
- if (sal.symtab != symtab || sal.line == 0)
- continue;
- begin = std::min (begin, sal.line);
- end = std::max (end, sal.line);
- }
- out:
- *pbegin = begin;
- *pend = end;
- }
- /* Print the source line information for a function call history line. */
- static void
- btrace_call_history_src_line (struct ui_out *uiout,
- const struct btrace_function *bfun)
- {
- struct symbol *sym;
- int begin, end;
- sym = bfun->sym;
- if (sym == NULL)
- return;
- uiout->field_string ("file",
- symtab_to_filename_for_display (symbol_symtab (sym)),
- file_name_style.style ());
- btrace_compute_src_line_range (bfun, &begin, &end);
- if (end < begin)
- return;
- uiout->text (":");
- uiout->field_signed ("min line", begin);
- if (end == begin)
- return;
- uiout->text (",");
- uiout->field_signed ("max line", end);
- }
- /* Get the name of a branch trace function. */
- static const char *
- btrace_get_bfun_name (const struct btrace_function *bfun)
- {
- struct minimal_symbol *msym;
- struct symbol *sym;
- if (bfun == NULL)
- return "??";
- msym = bfun->msym;
- sym = bfun->sym;
- if (sym != NULL)
- return sym->print_name ();
- else if (msym != NULL)
- return msym->print_name ();
- else
- return "??";
- }
- /* Disassemble a section of the recorded function trace. */
- static void
- btrace_call_history (struct ui_out *uiout,
- const struct btrace_thread_info *btinfo,
- const struct btrace_call_iterator *begin,
- const struct btrace_call_iterator *end,
- int int_flags)
- {
- struct btrace_call_iterator it;
- record_print_flags flags = (enum record_print_flag) int_flags;
- DEBUG ("ftrace (0x%x): [%u; %u)", int_flags, btrace_call_number (begin),
- btrace_call_number (end));
- for (it = *begin; btrace_call_cmp (&it, end) < 0; btrace_call_next (&it, 1))
- {
- const struct btrace_function *bfun;
- struct minimal_symbol *msym;
- struct symbol *sym;
- bfun = btrace_call_get (&it);
- sym = bfun->sym;
- msym = bfun->msym;
- /* Print the function index. */
- uiout->field_unsigned ("index", bfun->number);
- uiout->text ("\t");
- /* Indicate gaps in the trace. */
- if (bfun->errcode != 0)
- {
- const struct btrace_config *conf;
- conf = btrace_conf (btinfo);
- /* We have trace so we must have a configuration. */
- gdb_assert (conf != NULL);
- btrace_ui_out_decode_error (uiout, bfun->errcode, conf->format);
- continue;
- }
- if ((flags & RECORD_PRINT_INDENT_CALLS) != 0)
- {
- int level = bfun->level + btinfo->level, i;
- for (i = 0; i < level; ++i)
- uiout->text (" ");
- }
- if (sym != NULL)
- uiout->field_string ("function", sym->print_name (),
- function_name_style.style ());
- else if (msym != NULL)
- uiout->field_string ("function", msym->print_name (),
- function_name_style.style ());
- else if (!uiout->is_mi_like_p ())
- uiout->field_string ("function", "??",
- function_name_style.style ());
- if ((flags & RECORD_PRINT_INSN_RANGE) != 0)
- {
- uiout->text (_("\tinst "));
- btrace_call_history_insn_range (uiout, bfun);
- }
- if ((flags & RECORD_PRINT_SRC_LINE) != 0)
- {
- uiout->text (_("\tat "));
- btrace_call_history_src_line (uiout, bfun);
- }
- uiout->text ("\n");
- }
- }
- /* The call_history method of target record-btrace. */
- void
- record_btrace_target::call_history (int size, record_print_flags flags)
- {
- struct btrace_thread_info *btinfo;
- struct btrace_call_history *history;
- struct btrace_call_iterator begin, end;
- struct ui_out *uiout;
- unsigned int context, covered;
- uiout = current_uiout;
- ui_out_emit_tuple tuple_emitter (uiout, "insn history");
- context = abs (size);
- if (context == 0)
- error (_("Bad record function-call-history-size."));
- btinfo = require_btrace ();
- history = btinfo->call_history;
- if (history == NULL)
- {
- struct btrace_insn_iterator *replay;
- DEBUG ("call-history (0x%x): %d", (int) flags, size);
- /* If we're replaying, we start at the replay position. Otherwise, we
- start at the tail of the trace. */
- replay = btinfo->replay;
- if (replay != NULL)
- {
- begin.btinfo = btinfo;
- begin.index = replay->call_index;
- }
- else
- btrace_call_end (&begin, btinfo);
- /* We start from here and expand in the requested direction. Then we
- expand in the other direction, as well, to fill up any remaining
- context. */
- end = begin;
- if (size < 0)
- {
- /* We want the current position covered, as well. */
- covered = btrace_call_next (&end, 1);
- covered += btrace_call_prev (&begin, context - covered);
- covered += btrace_call_next (&end, context - covered);
- }
- else
- {
- covered = btrace_call_next (&end, context);
- covered += btrace_call_prev (&begin, context- covered);
- }
- }
- else
- {
- begin = history->begin;
- end = history->end;
- DEBUG ("call-history (0x%x): %d, prev: [%u; %u)", (int) flags, size,
- btrace_call_number (&begin), btrace_call_number (&end));
- if (size < 0)
- {
- end = begin;
- covered = btrace_call_prev (&begin, context);
- }
- else
- {
- begin = end;
- covered = btrace_call_next (&end, context);
- }
- }
- if (covered > 0)
- btrace_call_history (uiout, btinfo, &begin, &end, flags);
- else
- {
- if (size < 0)
- gdb_printf (_("At the start of the branch trace record.\n"));
- else
- gdb_printf (_("At the end of the branch trace record.\n"));
- }
- btrace_set_call_history (btinfo, &begin, &end);
- }
- /* The call_history_range method of target record-btrace. */
- void
- record_btrace_target::call_history_range (ULONGEST from, ULONGEST to,
- record_print_flags flags)
- {
- struct btrace_thread_info *btinfo;
- struct btrace_call_iterator begin, end;
- struct ui_out *uiout;
- unsigned int low, high;
- int found;
- uiout = current_uiout;
- ui_out_emit_tuple tuple_emitter (uiout, "func history");
- low = from;
- high = to;
- DEBUG ("call-history (0x%x): [%u; %u)", (int) flags, low, high);
- /* Check for wrap-arounds. */
- if (low != from || high != to)
- error (_("Bad range."));
- if (high < low)
- error (_("Bad range."));
- btinfo = require_btrace ();
- found = btrace_find_call_by_number (&begin, btinfo, low);
- if (found == 0)
- error (_("Range out of bounds."));
- found = btrace_find_call_by_number (&end, btinfo, high);
- if (found == 0)
- {
- /* Silently truncate the range. */
- btrace_call_end (&end, btinfo);
- }
- else
- {
- /* We want both begin and end to be inclusive. */
- btrace_call_next (&end, 1);
- }
- btrace_call_history (uiout, btinfo, &begin, &end, flags);
- btrace_set_call_history (btinfo, &begin, &end);
- }
- /* The call_history_from method of target record-btrace. */
- void
- record_btrace_target::call_history_from (ULONGEST from, int size,
- record_print_flags flags)
- {
- ULONGEST begin, end, context;
- context = abs (size);
- if (context == 0)
- error (_("Bad record function-call-history-size."));
- if (size < 0)
- {
- end = from;
- if (from < context)
- begin = 0;
- else
- begin = from - context + 1;
- }
- else
- {
- begin = from;
- end = from + context - 1;
- /* Check for wrap-around. */
- if (end < begin)
- end = ULONGEST_MAX;
- }
- call_history_range ( begin, end, flags);
- }
- /* The record_method method of target record-btrace. */
- enum record_method
- record_btrace_target::record_method (ptid_t ptid)
- {
- process_stratum_target *proc_target = current_inferior ()->process_target ();
- thread_info *const tp = find_thread_ptid (proc_target, ptid);
- if (tp == NULL)
- error (_("No thread."));
- if (tp->btrace.target == NULL)
- return RECORD_METHOD_NONE;
- return RECORD_METHOD_BTRACE;
- }
- /* The record_is_replaying method of target record-btrace. */
- bool
- record_btrace_target::record_is_replaying (ptid_t ptid)
- {
- process_stratum_target *proc_target = current_inferior ()->process_target ();
- for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
- if (btrace_is_replaying (tp))
- return true;
- return false;
- }
- /* The record_will_replay method of target record-btrace. */
- bool
- record_btrace_target::record_will_replay (ptid_t ptid, int dir)
- {
- return dir == EXEC_REVERSE || record_is_replaying (ptid);
- }
- /* The xfer_partial method of target record-btrace. */
- enum target_xfer_status
- record_btrace_target::xfer_partial (enum target_object object,
- const char *annex, gdb_byte *readbuf,
- const gdb_byte *writebuf, ULONGEST offset,
- ULONGEST len, ULONGEST *xfered_len)
- {
- /* Filter out requests that don't make sense during replay. */
- if (replay_memory_access == replay_memory_access_read_only
- && !record_btrace_generating_corefile
- && record_is_replaying (inferior_ptid))
- {
- switch (object)
- {
- case TARGET_OBJECT_MEMORY:
- {
- const struct target_section *section;
- /* We do not allow writing memory in general. */
- if (writebuf != NULL)
- {
- *xfered_len = len;
- return TARGET_XFER_UNAVAILABLE;
- }
- /* We allow reading readonly memory. */
- section = target_section_by_addr (this, offset);
- if (section != NULL)
- {
- /* Check if the section we found is readonly. */
- if ((bfd_section_flags (section->the_bfd_section)
- & SEC_READONLY) != 0)
- {
- /* Truncate the request to fit into this section. */
- len = std::min (len, section->endaddr - offset);
- break;
- }
- }
- *xfered_len = len;
- return TARGET_XFER_UNAVAILABLE;
- }
- }
- }
- /* Forward the request. */
- return this->beneath ()->xfer_partial (object, annex, readbuf, writebuf,
- offset, len, xfered_len);
- }
- /* The insert_breakpoint method of target record-btrace. */
- int
- record_btrace_target::insert_breakpoint (struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt)
- {
- const char *old;
- int ret;
- /* Inserting breakpoints requires accessing memory. Allow it for the
- duration of this function. */
- old = replay_memory_access;
- replay_memory_access = replay_memory_access_read_write;
- ret = 0;
- try
- {
- ret = this->beneath ()->insert_breakpoint (gdbarch, bp_tgt);
- }
- catch (const gdb_exception &except)
- {
- replay_memory_access = old;
- throw;
- }
- replay_memory_access = old;
- return ret;
- }
- /* The remove_breakpoint method of target record-btrace. */
- int
- record_btrace_target::remove_breakpoint (struct gdbarch *gdbarch,
- struct bp_target_info *bp_tgt,
- enum remove_bp_reason reason)
- {
- const char *old;
- int ret;
- /* Removing breakpoints requires accessing memory. Allow it for the
- duration of this function. */
- old = replay_memory_access;
- replay_memory_access = replay_memory_access_read_write;
- ret = 0;
- try
- {
- ret = this->beneath ()->remove_breakpoint (gdbarch, bp_tgt, reason);
- }
- catch (const gdb_exception &except)
- {
- replay_memory_access = old;
- throw;
- }
- replay_memory_access = old;
- return ret;
- }
- /* The fetch_registers method of target record-btrace. */
- void
- record_btrace_target::fetch_registers (struct regcache *regcache, int regno)
- {
- btrace_insn_iterator *replay = nullptr;
- /* Thread-db may ask for a thread's registers before GDB knows about the
- thread. We forward the request to the target beneath in this
- case. */
- thread_info *tp = find_thread_ptid (regcache->target (), regcache->ptid ());
- if (tp != nullptr)
- replay = tp->btrace.replay;
- if (replay != nullptr && !record_btrace_generating_corefile)
- {
- const struct btrace_insn *insn;
- struct gdbarch *gdbarch;
- int pcreg;
- gdbarch = regcache->arch ();
- pcreg = gdbarch_pc_regnum (gdbarch);
- if (pcreg < 0)
- return;
- /* We can only provide the PC register. */
- if (regno >= 0 && regno != pcreg)
- return;
- insn = btrace_insn_get (replay);
- gdb_assert (insn != NULL);
- regcache->raw_supply (regno, &insn->pc);
- }
- else
- this->beneath ()->fetch_registers (regcache, regno);
- }
- /* The store_registers method of target record-btrace. */
- void
- record_btrace_target::store_registers (struct regcache *regcache, int regno)
- {
- if (!record_btrace_generating_corefile
- && record_is_replaying (regcache->ptid ()))
- error (_("Cannot write registers while replaying."));
- gdb_assert (may_write_registers);
- this->beneath ()->store_registers (regcache, regno);
- }
- /* The prepare_to_store method of target record-btrace. */
- void
- record_btrace_target::prepare_to_store (struct regcache *regcache)
- {
- if (!record_btrace_generating_corefile
- && record_is_replaying (regcache->ptid ()))
- return;
- this->beneath ()->prepare_to_store (regcache);
- }
- /* The branch trace frame cache. */
- struct btrace_frame_cache
- {
- /* The thread. */
- struct thread_info *tp;
- /* The frame info. */
- struct frame_info *frame;
- /* The branch trace function segment. */
- const struct btrace_function *bfun;
- };
- /* A struct btrace_frame_cache hash table indexed by NEXT. */
- static htab_t bfcache;
- /* hash_f for htab_create_alloc of bfcache. */
- static hashval_t
- bfcache_hash (const void *arg)
- {
- const struct btrace_frame_cache *cache
- = (const struct btrace_frame_cache *) arg;
- return htab_hash_pointer (cache->frame);
- }
- /* eq_f for htab_create_alloc of bfcache. */
- static int
- bfcache_eq (const void *arg1, const void *arg2)
- {
- const struct btrace_frame_cache *cache1
- = (const struct btrace_frame_cache *) arg1;
- const struct btrace_frame_cache *cache2
- = (const struct btrace_frame_cache *) arg2;
- return cache1->frame == cache2->frame;
- }
- /* Create a new btrace frame cache. */
- static struct btrace_frame_cache *
- bfcache_new (struct frame_info *frame)
- {
- struct btrace_frame_cache *cache;
- void **slot;
- cache = FRAME_OBSTACK_ZALLOC (struct btrace_frame_cache);
- cache->frame = frame;
- slot = htab_find_slot (bfcache, cache, INSERT);
- gdb_assert (*slot == NULL);
- *slot = cache;
- return cache;
- }
- /* Extract the branch trace function from a branch trace frame. */
- static const struct btrace_function *
- btrace_get_frame_function (struct frame_info *frame)
- {
- const struct btrace_frame_cache *cache;
- struct btrace_frame_cache pattern;
- void **slot;
- pattern.frame = frame;
- slot = htab_find_slot (bfcache, &pattern, NO_INSERT);
- if (slot == NULL)
- return NULL;
- cache = (const struct btrace_frame_cache *) *slot;
- return cache->bfun;
- }
- /* Implement stop_reason method for record_btrace_frame_unwind. */
- static enum unwind_stop_reason
- record_btrace_frame_unwind_stop_reason (struct frame_info *this_frame,
- void **this_cache)
- {
- const struct btrace_frame_cache *cache;
- const struct btrace_function *bfun;
- cache = (const struct btrace_frame_cache *) *this_cache;
- bfun = cache->bfun;
- gdb_assert (bfun != NULL);
- if (bfun->up == 0)
- return UNWIND_UNAVAILABLE;
- return UNWIND_NO_REASON;
- }
- /* Implement this_id method for record_btrace_frame_unwind. */
- static void
- record_btrace_frame_this_id (struct frame_info *this_frame, void **this_cache,
- struct frame_id *this_id)
- {
- const struct btrace_frame_cache *cache;
- const struct btrace_function *bfun;
- struct btrace_call_iterator it;
- CORE_ADDR code, special;
- cache = (const struct btrace_frame_cache *) *this_cache;
- bfun = cache->bfun;
- gdb_assert (bfun != NULL);
- while (btrace_find_call_by_number (&it, &cache->tp->btrace, bfun->prev) != 0)
- bfun = btrace_call_get (&it);
- code = get_frame_func (this_frame);
- special = bfun->number;
- *this_id = frame_id_build_unavailable_stack_special (code, special);
- DEBUG ("[frame] %s id: (!stack, pc=%s, special=%s)",
- btrace_get_bfun_name (cache->bfun),
- core_addr_to_string_nz (this_id->code_addr),
- core_addr_to_string_nz (this_id->special_addr));
- }
- /* Implement prev_register method for record_btrace_frame_unwind. */
- static struct value *
- record_btrace_frame_prev_register (struct frame_info *this_frame,
- void **this_cache,
- int regnum)
- {
- const struct btrace_frame_cache *cache;
- const struct btrace_function *bfun, *caller;
- struct btrace_call_iterator it;
- struct gdbarch *gdbarch;
- CORE_ADDR pc;
- int pcreg;
- gdbarch = get_frame_arch (this_frame);
- pcreg = gdbarch_pc_regnum (gdbarch);
- if (pcreg < 0 || regnum != pcreg)
- throw_error (NOT_AVAILABLE_ERROR,
- _("Registers are not available in btrace record history"));
- cache = (const struct btrace_frame_cache *) *this_cache;
- bfun = cache->bfun;
- gdb_assert (bfun != NULL);
- if (btrace_find_call_by_number (&it, &cache->tp->btrace, bfun->up) == 0)
- throw_error (NOT_AVAILABLE_ERROR,
- _("No caller in btrace record history"));
- caller = btrace_call_get (&it);
- if ((bfun->flags & BFUN_UP_LINKS_TO_RET) != 0)
- pc = caller->insn.front ().pc;
- else
- {
- pc = caller->insn.back ().pc;
- pc += gdb_insn_length (gdbarch, pc);
- }
- DEBUG ("[frame] unwound PC in %s on level %d: %s",
- btrace_get_bfun_name (bfun), bfun->level,
- core_addr_to_string_nz (pc));
- return frame_unwind_got_address (this_frame, regnum, pc);
- }
- /* Implement sniffer method for record_btrace_frame_unwind. */
- static int
- record_btrace_frame_sniffer (const struct frame_unwind *self,
- struct frame_info *this_frame,
- void **this_cache)
- {
- const struct btrace_function *bfun;
- struct btrace_frame_cache *cache;
- struct thread_info *tp;
- struct frame_info *next;
- /* THIS_FRAME does not contain a reference to its thread. */
- tp = inferior_thread ();
- bfun = NULL;
- next = get_next_frame (this_frame);
- if (next == NULL)
- {
- const struct btrace_insn_iterator *replay;
- replay = tp->btrace.replay;
- if (replay != NULL)
- bfun = &replay->btinfo->functions[replay->call_index];
- }
- else
- {
- const struct btrace_function *callee;
- struct btrace_call_iterator it;
- callee = btrace_get_frame_function (next);
- if (callee == NULL || (callee->flags & BFUN_UP_LINKS_TO_TAILCALL) != 0)
- return 0;
- if (btrace_find_call_by_number (&it, &tp->btrace, callee->up) == 0)
- return 0;
- bfun = btrace_call_get (&it);
- }
- if (bfun == NULL)
- return 0;
- DEBUG ("[frame] sniffed frame for %s on level %d",
- btrace_get_bfun_name (bfun), bfun->level);
- /* This is our frame. Initialize the frame cache. */
- cache = bfcache_new (this_frame);
- cache->tp = tp;
- cache->bfun = bfun;
- *this_cache = cache;
- return 1;
- }
- /* Implement sniffer method for record_btrace_tailcall_frame_unwind. */
- static int
- record_btrace_tailcall_frame_sniffer (const struct frame_unwind *self,
- struct frame_info *this_frame,
- void **this_cache)
- {
- const struct btrace_function *bfun, *callee;
- struct btrace_frame_cache *cache;
- struct btrace_call_iterator it;
- struct frame_info *next;
- struct thread_info *tinfo;
- next = get_next_frame (this_frame);
- if (next == NULL)
- return 0;
- callee = btrace_get_frame_function (next);
- if (callee == NULL)
- return 0;
- if ((callee->flags & BFUN_UP_LINKS_TO_TAILCALL) == 0)
- return 0;
- tinfo = inferior_thread ();
- if (btrace_find_call_by_number (&it, &tinfo->btrace, callee->up) == 0)
- return 0;
- bfun = btrace_call_get (&it);
- DEBUG ("[frame] sniffed tailcall frame for %s on level %d",
- btrace_get_bfun_name (bfun), bfun->level);
- /* This is our frame. Initialize the frame cache. */
- cache = bfcache_new (this_frame);
- cache->tp = tinfo;
- cache->bfun = bfun;
- *this_cache = cache;
- return 1;
- }
- static void
- record_btrace_frame_dealloc_cache (struct frame_info *self, void *this_cache)
- {
- struct btrace_frame_cache *cache;
- void **slot;
- cache = (struct btrace_frame_cache *) this_cache;
- slot = htab_find_slot (bfcache, cache, NO_INSERT);
- gdb_assert (slot != NULL);
- htab_remove_elt (bfcache, cache);
- }
- /* btrace recording does not store previous memory content, neither the stack
- frames content. Any unwinding would return erroneous results as the stack
- contents no longer matches the changed PC value restored from history.
- Therefore this unwinder reports any possibly unwound registers as
- <unavailable>. */
- const struct frame_unwind record_btrace_frame_unwind =
- {
- "record-btrace",
- NORMAL_FRAME,
- record_btrace_frame_unwind_stop_reason,
- record_btrace_frame_this_id,
- record_btrace_frame_prev_register,
- NULL,
- record_btrace_frame_sniffer,
- record_btrace_frame_dealloc_cache
- };
- const struct frame_unwind record_btrace_tailcall_frame_unwind =
- {
- "record-btrace tailcall",
- TAILCALL_FRAME,
- record_btrace_frame_unwind_stop_reason,
- record_btrace_frame_this_id,
- record_btrace_frame_prev_register,
- NULL,
- record_btrace_tailcall_frame_sniffer,
- record_btrace_frame_dealloc_cache
- };
- /* Implement the get_unwinder method. */
- const struct frame_unwind *
- record_btrace_target::get_unwinder ()
- {
- return &record_btrace_frame_unwind;
- }
- /* Implement the get_tailcall_unwinder method. */
- const struct frame_unwind *
- record_btrace_target::get_tailcall_unwinder ()
- {
- return &record_btrace_tailcall_frame_unwind;
- }
- /* Return a human-readable string for FLAG. */
- static const char *
- btrace_thread_flag_to_str (btrace_thread_flags flag)
- {
- switch (flag)
- {
- case BTHR_STEP:
- return "step";
- case BTHR_RSTEP:
- return "reverse-step";
- case BTHR_CONT:
- return "cont";
- case BTHR_RCONT:
- return "reverse-cont";
- case BTHR_STOP:
- return "stop";
- }
- return "<invalid>";
- }
- /* Indicate that TP should be resumed according to FLAG. */
- static void
- record_btrace_resume_thread (struct thread_info *tp,
- enum btrace_thread_flag flag)
- {
- struct btrace_thread_info *btinfo;
- DEBUG ("resuming thread %s (%s): %x (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str (), flag,
- btrace_thread_flag_to_str (flag));
- btinfo = &tp->btrace;
- /* Fetch the latest branch trace. */
- btrace_fetch (tp, record_btrace_get_cpu ());
- /* A resume request overwrites a preceding resume or stop request. */
- btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
- btinfo->flags |= flag;
- }
- /* Get the current frame for TP. */
- static struct frame_id
- get_thread_current_frame_id (struct thread_info *tp)
- {
- /* Set current thread, which is implicitly used by
- get_current_frame. */
- scoped_restore_current_thread restore_thread;
- switch_to_thread (tp);
- process_stratum_target *proc_target = tp->inf->process_target ();
- /* Clear the executing flag to allow changes to the current frame.
- We are not actually running, yet. We just started a reverse execution
- command or a record goto command.
- For the latter, EXECUTING is false and this has no effect.
- For the former, EXECUTING is true and we're in wait, about to
- move the thread. Since we need to recompute the stack, we temporarily
- set EXECUTING to false. */
- bool executing = tp->executing ();
- set_executing (proc_target, inferior_ptid, false);
- SCOPE_EXIT
- {
- set_executing (proc_target, inferior_ptid, executing);
- };
- return get_frame_id (get_current_frame ());
- }
- /* Start replaying a thread. */
- static struct btrace_insn_iterator *
- record_btrace_start_replaying (struct thread_info *tp)
- {
- struct btrace_insn_iterator *replay;
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- replay = NULL;
- /* We can't start replaying without trace. */
- if (btinfo->functions.empty ())
- return NULL;
- /* GDB stores the current frame_id when stepping in order to detects steps
- into subroutines.
- Since frames are computed differently when we're replaying, we need to
- recompute those stored frames and fix them up so we can still detect
- subroutines after we started replaying. */
- try
- {
- struct frame_id frame_id;
- int upd_step_frame_id, upd_step_stack_frame_id;
- /* The current frame without replaying - computed via normal unwind. */
- frame_id = get_thread_current_frame_id (tp);
- /* Check if we need to update any stepping-related frame id's. */
- upd_step_frame_id = frame_id_eq (frame_id,
- tp->control.step_frame_id);
- upd_step_stack_frame_id = frame_id_eq (frame_id,
- tp->control.step_stack_frame_id);
- /* We start replaying at the end of the branch trace. This corresponds
- to the current instruction. */
- replay = XNEW (struct btrace_insn_iterator);
- btrace_insn_end (replay, btinfo);
- /* Skip gaps at the end of the trace. */
- while (btrace_insn_get (replay) == NULL)
- {
- unsigned int steps;
- steps = btrace_insn_prev (replay, 1);
- if (steps == 0)
- error (_("No trace."));
- }
- /* We're not replaying, yet. */
- gdb_assert (btinfo->replay == NULL);
- btinfo->replay = replay;
- /* Make sure we're not using any stale registers. */
- registers_changed_thread (tp);
- /* The current frame with replaying - computed via btrace unwind. */
- frame_id = get_thread_current_frame_id (tp);
- /* Replace stepping related frames where necessary. */
- if (upd_step_frame_id)
- tp->control.step_frame_id = frame_id;
- if (upd_step_stack_frame_id)
- tp->control.step_stack_frame_id = frame_id;
- }
- catch (const gdb_exception &except)
- {
- xfree (btinfo->replay);
- btinfo->replay = NULL;
- registers_changed_thread (tp);
- throw;
- }
- return replay;
- }
- /* Stop replaying a thread. */
- static void
- record_btrace_stop_replaying (struct thread_info *tp)
- {
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- xfree (btinfo->replay);
- btinfo->replay = NULL;
- /* Make sure we're not leaving any stale registers. */
- registers_changed_thread (tp);
- }
- /* Stop replaying TP if it is at the end of its execution history. */
- static void
- record_btrace_stop_replaying_at_end (struct thread_info *tp)
- {
- struct btrace_insn_iterator *replay, end;
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- replay = btinfo->replay;
- if (replay == NULL)
- return;
- btrace_insn_end (&end, btinfo);
- if (btrace_insn_cmp (replay, &end) == 0)
- record_btrace_stop_replaying (tp);
- }
- /* The resume method of target record-btrace. */
- void
- record_btrace_target::resume (ptid_t ptid, int step, enum gdb_signal signal)
- {
- enum btrace_thread_flag flag, cflag;
- DEBUG ("resume %s: %s%s", ptid.to_string ().c_str (),
- ::execution_direction == EXEC_REVERSE ? "reverse-" : "",
- step ? "step" : "cont");
- /* Store the execution direction of the last resume.
- If there is more than one resume call, we have to rely on infrun
- to not change the execution direction in-between. */
- record_btrace_resume_exec_dir = ::execution_direction;
- /* As long as we're not replaying, just forward the request.
- For non-stop targets this means that no thread is replaying. In order to
- make progress, we may need to explicitly move replaying threads to the end
- of their execution history. */
- if ((::execution_direction != EXEC_REVERSE)
- && !record_is_replaying (minus_one_ptid))
- {
- this->beneath ()->resume (ptid, step, signal);
- return;
- }
- /* Compute the btrace thread flag for the requested move. */
- if (::execution_direction == EXEC_REVERSE)
- {
- flag = step == 0 ? BTHR_RCONT : BTHR_RSTEP;
- cflag = BTHR_RCONT;
- }
- else
- {
- flag = step == 0 ? BTHR_CONT : BTHR_STEP;
- cflag = BTHR_CONT;
- }
- /* We just indicate the resume intent here. The actual stepping happens in
- record_btrace_wait below.
- For all-stop targets, we only step INFERIOR_PTID and continue others. */
- process_stratum_target *proc_target = current_inferior ()->process_target ();
- if (!target_is_non_stop_p ())
- {
- gdb_assert (inferior_ptid.matches (ptid));
- for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
- {
- if (tp->ptid.matches (inferior_ptid))
- record_btrace_resume_thread (tp, flag);
- else
- record_btrace_resume_thread (tp, cflag);
- }
- }
- else
- {
- for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
- record_btrace_resume_thread (tp, flag);
- }
- /* Async support. */
- if (target_can_async_p ())
- {
- target_async (1);
- mark_async_event_handler (record_btrace_async_inferior_event_handler);
- }
- }
- /* Cancel resuming TP. */
- static void
- record_btrace_cancel_resume (struct thread_info *tp)
- {
- btrace_thread_flags flags;
- flags = tp->btrace.flags & (BTHR_MOVE | BTHR_STOP);
- if (flags == 0)
- return;
- DEBUG ("cancel resume thread %s (%s): %x (%s)",
- print_thread_id (tp),
- tp->ptid.to_string ().c_str (), flags.raw (),
- btrace_thread_flag_to_str (flags));
- tp->btrace.flags &= ~(BTHR_MOVE | BTHR_STOP);
- record_btrace_stop_replaying_at_end (tp);
- }
- /* Return a target_waitstatus indicating that we ran out of history. */
- static struct target_waitstatus
- btrace_step_no_history (void)
- {
- struct target_waitstatus status;
- status.set_no_history ();
- return status;
- }
- /* Return a target_waitstatus indicating that a step finished. */
- static struct target_waitstatus
- btrace_step_stopped (void)
- {
- struct target_waitstatus status;
- status.set_stopped (GDB_SIGNAL_TRAP);
- return status;
- }
- /* Return a target_waitstatus indicating that a thread was stopped as
- requested. */
- static struct target_waitstatus
- btrace_step_stopped_on_request (void)
- {
- struct target_waitstatus status;
- status.set_stopped (GDB_SIGNAL_0);
- return status;
- }
- /* Return a target_waitstatus indicating a spurious stop. */
- static struct target_waitstatus
- btrace_step_spurious (void)
- {
- struct target_waitstatus status;
- status.set_spurious ();
- return status;
- }
- /* Return a target_waitstatus indicating that the thread was not resumed. */
- static struct target_waitstatus
- btrace_step_no_resumed (void)
- {
- struct target_waitstatus status;
- status.set_no_resumed ();
- return status;
- }
- /* Return a target_waitstatus indicating that we should wait again. */
- static struct target_waitstatus
- btrace_step_again (void)
- {
- struct target_waitstatus status;
- status.set_ignore ();
- return status;
- }
- /* Clear the record histories. */
- static void
- record_btrace_clear_histories (struct btrace_thread_info *btinfo)
- {
- xfree (btinfo->insn_history);
- xfree (btinfo->call_history);
- btinfo->insn_history = NULL;
- btinfo->call_history = NULL;
- }
- /* Check whether TP's current replay position is at a breakpoint. */
- static int
- record_btrace_replay_at_breakpoint (struct thread_info *tp)
- {
- struct btrace_insn_iterator *replay;
- struct btrace_thread_info *btinfo;
- const struct btrace_insn *insn;
- btinfo = &tp->btrace;
- replay = btinfo->replay;
- if (replay == NULL)
- return 0;
- insn = btrace_insn_get (replay);
- if (insn == NULL)
- return 0;
- return record_check_stopped_by_breakpoint (tp->inf->aspace, insn->pc,
- &btinfo->stop_reason);
- }
- /* Step one instruction in forward direction. */
- static struct target_waitstatus
- record_btrace_single_step_forward (struct thread_info *tp)
- {
- struct btrace_insn_iterator *replay, end, start;
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- replay = btinfo->replay;
- /* We're done if we're not replaying. */
- if (replay == NULL)
- return btrace_step_no_history ();
- /* Check if we're stepping a breakpoint. */
- if (record_btrace_replay_at_breakpoint (tp))
- return btrace_step_stopped ();
- /* Skip gaps during replay. If we end up at a gap (at the end of the trace),
- jump back to the instruction at which we started. */
- start = *replay;
- do
- {
- unsigned int steps;
- /* We will bail out here if we continue stepping after reaching the end
- of the execution history. */
- steps = btrace_insn_next (replay, 1);
- if (steps == 0)
- {
- *replay = start;
- return btrace_step_no_history ();
- }
- }
- while (btrace_insn_get (replay) == NULL);
- /* Determine the end of the instruction trace. */
- btrace_insn_end (&end, btinfo);
- /* The execution trace contains (and ends with) the current instruction.
- This instruction has not been executed, yet, so the trace really ends
- one instruction earlier. */
- if (btrace_insn_cmp (replay, &end) == 0)
- return btrace_step_no_history ();
- return btrace_step_spurious ();
- }
- /* Step one instruction in backward direction. */
- static struct target_waitstatus
- record_btrace_single_step_backward (struct thread_info *tp)
- {
- struct btrace_insn_iterator *replay, start;
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- replay = btinfo->replay;
- /* Start replaying if we're not already doing so. */
- if (replay == NULL)
- replay = record_btrace_start_replaying (tp);
- /* If we can't step any further, we reached the end of the history.
- Skip gaps during replay. If we end up at a gap (at the beginning of
- the trace), jump back to the instruction at which we started. */
- start = *replay;
- do
- {
- unsigned int steps;
- steps = btrace_insn_prev (replay, 1);
- if (steps == 0)
- {
- *replay = start;
- return btrace_step_no_history ();
- }
- }
- while (btrace_insn_get (replay) == NULL);
- /* Check if we're stepping a breakpoint.
- For reverse-stepping, this check is after the step. There is logic in
- infrun.c that handles reverse-stepping separately. See, for example,
- proceed and adjust_pc_after_break.
- This code assumes that for reverse-stepping, PC points to the last
- de-executed instruction, whereas for forward-stepping PC points to the
- next to-be-executed instruction. */
- if (record_btrace_replay_at_breakpoint (tp))
- return btrace_step_stopped ();
- return btrace_step_spurious ();
- }
- /* Step a single thread. */
- static struct target_waitstatus
- record_btrace_step_thread (struct thread_info *tp)
- {
- struct btrace_thread_info *btinfo;
- struct target_waitstatus status;
- btrace_thread_flags flags;
- btinfo = &tp->btrace;
- flags = btinfo->flags & (BTHR_MOVE | BTHR_STOP);
- btinfo->flags &= ~(BTHR_MOVE | BTHR_STOP);
- DEBUG ("stepping thread %s (%s): %x (%s)", print_thread_id (tp),
- tp->ptid.to_string ().c_str (), flags.raw (),
- btrace_thread_flag_to_str (flags));
- /* We can't step without an execution history. */
- if ((flags & BTHR_MOVE) != 0 && btrace_is_empty (tp))
- return btrace_step_no_history ();
- switch (flags)
- {
- default:
- internal_error (__FILE__, __LINE__, _("invalid stepping type."));
- case BTHR_STOP:
- return btrace_step_stopped_on_request ();
- case BTHR_STEP:
- status = record_btrace_single_step_forward (tp);
- if (status.kind () != TARGET_WAITKIND_SPURIOUS)
- break;
- return btrace_step_stopped ();
- case BTHR_RSTEP:
- status = record_btrace_single_step_backward (tp);
- if (status.kind () != TARGET_WAITKIND_SPURIOUS)
- break;
- return btrace_step_stopped ();
- case BTHR_CONT:
- status = record_btrace_single_step_forward (tp);
- if (status.kind () != TARGET_WAITKIND_SPURIOUS)
- break;
- btinfo->flags |= flags;
- return btrace_step_again ();
- case BTHR_RCONT:
- status = record_btrace_single_step_backward (tp);
- if (status.kind () != TARGET_WAITKIND_SPURIOUS)
- break;
- btinfo->flags |= flags;
- return btrace_step_again ();
- }
- /* We keep threads moving at the end of their execution history. The wait
- method will stop the thread for whom the event is reported. */
- if (status.kind () == TARGET_WAITKIND_NO_HISTORY)
- btinfo->flags |= flags;
- return status;
- }
- /* Announce further events if necessary. */
- static void
- record_btrace_maybe_mark_async_event
- (const std::vector<thread_info *> &moving,
- const std::vector<thread_info *> &no_history)
- {
- bool more_moving = !moving.empty ();
- bool more_no_history = !no_history.empty ();;
- if (!more_moving && !more_no_history)
- return;
- if (more_moving)
- DEBUG ("movers pending");
- if (more_no_history)
- DEBUG ("no-history pending");
- mark_async_event_handler (record_btrace_async_inferior_event_handler);
- }
- /* The wait method of target record-btrace. */
- ptid_t
- record_btrace_target::wait (ptid_t ptid, struct target_waitstatus *status,
- target_wait_flags options)
- {
- std::vector<thread_info *> moving;
- std::vector<thread_info *> no_history;
- /* Clear this, if needed we'll re-mark it below. */
- clear_async_event_handler (record_btrace_async_inferior_event_handler);
- DEBUG ("wait %s (0x%x)", ptid.to_string ().c_str (),
- (unsigned) options);
- /* As long as we're not replaying, just forward the request. */
- if ((::execution_direction != EXEC_REVERSE)
- && !record_is_replaying (minus_one_ptid))
- {
- return this->beneath ()->wait (ptid, status, options);
- }
- /* Keep a work list of moving threads. */
- process_stratum_target *proc_target = current_inferior ()->process_target ();
- for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
- if ((tp->btrace.flags & (BTHR_MOVE | BTHR_STOP)) != 0)
- moving.push_back (tp);
- if (moving.empty ())
- {
- *status = btrace_step_no_resumed ();
- DEBUG ("wait ended by %s: %s", null_ptid.to_string ().c_str (),
- status->to_string ().c_str ());
- return null_ptid;
- }
- /* Step moving threads one by one, one step each, until either one thread
- reports an event or we run out of threads to step.
- When stepping more than one thread, chances are that some threads reach
- the end of their execution history earlier than others. If we reported
- this immediately, all-stop on top of non-stop would stop all threads and
- resume the same threads next time. And we would report the same thread
- having reached the end of its execution history again.
- In the worst case, this would starve the other threads. But even if other
- threads would be allowed to make progress, this would result in far too
- many intermediate stops.
- We therefore delay the reporting of "no execution history" until we have
- nothing else to report. By this time, all threads should have moved to
- either the beginning or the end of their execution history. There will
- be a single user-visible stop. */
- struct thread_info *eventing = NULL;
- while ((eventing == NULL) && !moving.empty ())
- {
- for (unsigned int ix = 0; eventing == NULL && ix < moving.size ();)
- {
- thread_info *tp = moving[ix];
- *status = record_btrace_step_thread (tp);
- switch (status->kind ())
- {
- case TARGET_WAITKIND_IGNORE:
- ix++;
- break;
- case TARGET_WAITKIND_NO_HISTORY:
- no_history.push_back (ordered_remove (moving, ix));
- break;
- default:
- eventing = unordered_remove (moving, ix);
- break;
- }
- }
- }
- if (eventing == NULL)
- {
- /* We started with at least one moving thread. This thread must have
- either stopped or reached the end of its execution history.
- In the former case, EVENTING must not be NULL.
- In the latter case, NO_HISTORY must not be empty. */
- gdb_assert (!no_history.empty ());
- /* We kept threads moving at the end of their execution history. Stop
- EVENTING now that we are going to report its stop. */
- eventing = unordered_remove (no_history, 0);
- eventing->btrace.flags &= ~BTHR_MOVE;
- *status = btrace_step_no_history ();
- }
- gdb_assert (eventing != NULL);
- /* We kept threads replaying at the end of their execution history. Stop
- replaying EVENTING now that we are going to report its stop. */
- record_btrace_stop_replaying_at_end (eventing);
- /* Stop all other threads. */
- if (!target_is_non_stop_p ())
- {
- for (thread_info *tp : current_inferior ()->non_exited_threads ())
- record_btrace_cancel_resume (tp);
- }
- /* In async mode, we need to announce further events. */
- if (target_is_async_p ())
- record_btrace_maybe_mark_async_event (moving, no_history);
- /* Start record histories anew from the current position. */
- record_btrace_clear_histories (&eventing->btrace);
- /* We moved the replay position but did not update registers. */
- registers_changed_thread (eventing);
- DEBUG ("wait ended by thread %s (%s): %s",
- print_thread_id (eventing),
- eventing->ptid.to_string ().c_str (),
- status->to_string ().c_str ());
- return eventing->ptid;
- }
- /* The stop method of target record-btrace. */
- void
- record_btrace_target::stop (ptid_t ptid)
- {
- DEBUG ("stop %s", ptid.to_string ().c_str ());
- /* As long as we're not replaying, just forward the request. */
- if ((::execution_direction != EXEC_REVERSE)
- && !record_is_replaying (minus_one_ptid))
- {
- this->beneath ()->stop (ptid);
- }
- else
- {
- process_stratum_target *proc_target
- = current_inferior ()->process_target ();
- for (thread_info *tp : all_non_exited_threads (proc_target, ptid))
- {
- tp->btrace.flags &= ~BTHR_MOVE;
- tp->btrace.flags |= BTHR_STOP;
- }
- }
- }
- /* The can_execute_reverse method of target record-btrace. */
- bool
- record_btrace_target::can_execute_reverse ()
- {
- return true;
- }
- /* The stopped_by_sw_breakpoint method of target record-btrace. */
- bool
- record_btrace_target::stopped_by_sw_breakpoint ()
- {
- if (record_is_replaying (minus_one_ptid))
- {
- struct thread_info *tp = inferior_thread ();
- return tp->btrace.stop_reason == TARGET_STOPPED_BY_SW_BREAKPOINT;
- }
- return this->beneath ()->stopped_by_sw_breakpoint ();
- }
- /* The supports_stopped_by_sw_breakpoint method of target
- record-btrace. */
- bool
- record_btrace_target::supports_stopped_by_sw_breakpoint ()
- {
- if (record_is_replaying (minus_one_ptid))
- return true;
- return this->beneath ()->supports_stopped_by_sw_breakpoint ();
- }
- /* The stopped_by_sw_breakpoint method of target record-btrace. */
- bool
- record_btrace_target::stopped_by_hw_breakpoint ()
- {
- if (record_is_replaying (minus_one_ptid))
- {
- struct thread_info *tp = inferior_thread ();
- return tp->btrace.stop_reason == TARGET_STOPPED_BY_HW_BREAKPOINT;
- }
- return this->beneath ()->stopped_by_hw_breakpoint ();
- }
- /* The supports_stopped_by_hw_breakpoint method of target
- record-btrace. */
- bool
- record_btrace_target::supports_stopped_by_hw_breakpoint ()
- {
- if (record_is_replaying (minus_one_ptid))
- return true;
- return this->beneath ()->supports_stopped_by_hw_breakpoint ();
- }
- /* The update_thread_list method of target record-btrace. */
- void
- record_btrace_target::update_thread_list ()
- {
- /* We don't add or remove threads during replay. */
- if (record_is_replaying (minus_one_ptid))
- return;
- /* Forward the request. */
- this->beneath ()->update_thread_list ();
- }
- /* The thread_alive method of target record-btrace. */
- bool
- record_btrace_target::thread_alive (ptid_t ptid)
- {
- /* We don't add or remove threads during replay. */
- if (record_is_replaying (minus_one_ptid))
- return true;
- /* Forward the request. */
- return this->beneath ()->thread_alive (ptid);
- }
- /* Set the replay branch trace instruction iterator. If IT is NULL, replay
- is stopped. */
- static void
- record_btrace_set_replay (struct thread_info *tp,
- const struct btrace_insn_iterator *it)
- {
- struct btrace_thread_info *btinfo;
- btinfo = &tp->btrace;
- if (it == NULL)
- record_btrace_stop_replaying (tp);
- else
- {
- if (btinfo->replay == NULL)
- record_btrace_start_replaying (tp);
- else if (btrace_insn_cmp (btinfo->replay, it) == 0)
- return;
- *btinfo->replay = *it;
- registers_changed_thread (tp);
- }
- /* Start anew from the new replay position. */
- record_btrace_clear_histories (btinfo);
- inferior_thread ()->set_stop_pc (regcache_read_pc (get_current_regcache ()));
- print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC, 1);
- }
- /* The goto_record_begin method of target record-btrace. */
- void
- record_btrace_target::goto_record_begin ()
- {
- struct thread_info *tp;
- struct btrace_insn_iterator begin;
- tp = require_btrace_thread ();
- btrace_insn_begin (&begin, &tp->btrace);
- /* Skip gaps at the beginning of the trace. */
- while (btrace_insn_get (&begin) == NULL)
- {
- unsigned int steps;
- steps = btrace_insn_next (&begin, 1);
- if (steps == 0)
- error (_("No trace."));
- }
- record_btrace_set_replay (tp, &begin);
- }
- /* The goto_record_end method of target record-btrace. */
- void
- record_btrace_target::goto_record_end ()
- {
- struct thread_info *tp;
- tp = require_btrace_thread ();
- record_btrace_set_replay (tp, NULL);
- }
- /* The goto_record method of target record-btrace. */
- void
- record_btrace_target::goto_record (ULONGEST insn)
- {
- struct thread_info *tp;
- struct btrace_insn_iterator it;
- unsigned int number;
- int found;
- number = insn;
- /* Check for wrap-arounds. */
- if (number != insn)
- error (_("Instruction number out of range."));
- tp = require_btrace_thread ();
- found = btrace_find_insn_by_number (&it, &tp->btrace, number);
- /* Check if the instruction could not be found or is a gap. */
- if (found == 0 || btrace_insn_get (&it) == NULL)
- error (_("No such instruction."));
- record_btrace_set_replay (tp, &it);
- }
- /* The record_stop_replaying method of target record-btrace. */
- void
- record_btrace_target::record_stop_replaying ()
- {
- for (thread_info *tp : current_inferior ()->non_exited_threads ())
- record_btrace_stop_replaying (tp);
- }
- /* The execution_direction target method. */
- enum exec_direction_kind
- record_btrace_target::execution_direction ()
- {
- return record_btrace_resume_exec_dir;
- }
- /* The prepare_to_generate_core target method. */
- void
- record_btrace_target::prepare_to_generate_core ()
- {
- record_btrace_generating_corefile = 1;
- }
- /* The done_generating_core target method. */
- void
- record_btrace_target::done_generating_core ()
- {
- record_btrace_generating_corefile = 0;
- }
- /* Start recording in BTS format. */
- static void
- cmd_record_btrace_bts_start (const char *args, int from_tty)
- {
- if (args != NULL && *args != 0)
- error (_("Invalid argument."));
- record_btrace_conf.format = BTRACE_FORMAT_BTS;
- try
- {
- execute_command ("target record-btrace", from_tty);
- }
- catch (const gdb_exception &exception)
- {
- record_btrace_conf.format = BTRACE_FORMAT_NONE;
- throw;
- }
- }
- /* Start recording in Intel Processor Trace format. */
- static void
- cmd_record_btrace_pt_start (const char *args, int from_tty)
- {
- if (args != NULL && *args != 0)
- error (_("Invalid argument."));
- record_btrace_conf.format = BTRACE_FORMAT_PT;
- try
- {
- execute_command ("target record-btrace", from_tty);
- }
- catch (const gdb_exception &exception)
- {
- record_btrace_conf.format = BTRACE_FORMAT_NONE;
- throw;
- }
- }
- /* Alias for "target record". */
- static void
- cmd_record_btrace_start (const char *args, int from_tty)
- {
- if (args != NULL && *args != 0)
- error (_("Invalid argument."));
- record_btrace_conf.format = BTRACE_FORMAT_PT;
- try
- {
- execute_command ("target record-btrace", from_tty);
- }
- catch (const gdb_exception &exception)
- {
- record_btrace_conf.format = BTRACE_FORMAT_BTS;
- try
- {
- execute_command ("target record-btrace", from_tty);
- }
- catch (const gdb_exception &ex)
- {
- record_btrace_conf.format = BTRACE_FORMAT_NONE;
- throw;
- }
- }
- }
- /* The "show record btrace replay-memory-access" command. */
- static void
- cmd_show_replay_memory_access (struct ui_file *file, int from_tty,
- struct cmd_list_element *c, const char *value)
- {
- gdb_printf (file, _("Replay memory access is %s.\n"),
- replay_memory_access);
- }
- /* The "set record btrace cpu none" command. */
- static void
- cmd_set_record_btrace_cpu_none (const char *args, int from_tty)
- {
- if (args != nullptr && *args != 0)
- error (_("Trailing junk: '%s'."), args);
- record_btrace_cpu_state = CS_NONE;
- }
- /* The "set record btrace cpu auto" command. */
- static void
- cmd_set_record_btrace_cpu_auto (const char *args, int from_tty)
- {
- if (args != nullptr && *args != 0)
- error (_("Trailing junk: '%s'."), args);
- record_btrace_cpu_state = CS_AUTO;
- }
- /* The "set record btrace cpu" command. */
- static void
- cmd_set_record_btrace_cpu (const char *args, int from_tty)
- {
- if (args == nullptr)
- args = "";
- /* We use a hard-coded vendor string for now. */
- unsigned int family, model, stepping;
- int l1, l2, matches = sscanf (args, "intel: %u/%u%n/%u%n", &family,
- &model, &l1, &stepping, &l2);
- if (matches == 3)
- {
- if (strlen (args) != l2)
- error (_("Trailing junk: '%s'."), args + l2);
- }
- else if (matches == 2)
- {
- if (strlen (args) != l1)
- error (_("Trailing junk: '%s'."), args + l1);
- stepping = 0;
- }
- else
- error (_("Bad format. See \"help set record btrace cpu\"."));
- if (USHRT_MAX < family)
- error (_("Cpu family too big."));
- if (UCHAR_MAX < model)
- error (_("Cpu model too big."));
- if (UCHAR_MAX < stepping)
- error (_("Cpu stepping too big."));
- record_btrace_cpu.vendor = CV_INTEL;
- record_btrace_cpu.family = family;
- record_btrace_cpu.model = model;
- record_btrace_cpu.stepping = stepping;
- record_btrace_cpu_state = CS_CPU;
- }
- /* The "show record btrace cpu" command. */
- static void
- cmd_show_record_btrace_cpu (const char *args, int from_tty)
- {
- if (args != nullptr && *args != 0)
- error (_("Trailing junk: '%s'."), args);
- switch (record_btrace_cpu_state)
- {
- case CS_AUTO:
- gdb_printf (_("btrace cpu is 'auto'.\n"));
- return;
- case CS_NONE:
- gdb_printf (_("btrace cpu is 'none'.\n"));
- return;
- case CS_CPU:
- switch (record_btrace_cpu.vendor)
- {
- case CV_INTEL:
- if (record_btrace_cpu.stepping == 0)
- gdb_printf (_("btrace cpu is 'intel: %u/%u'.\n"),
- record_btrace_cpu.family,
- record_btrace_cpu.model);
- else
- gdb_printf (_("btrace cpu is 'intel: %u/%u/%u'.\n"),
- record_btrace_cpu.family,
- record_btrace_cpu.model,
- record_btrace_cpu.stepping);
- return;
- }
- }
- error (_("Internal error: bad cpu state."));
- }
- /* The "record bts buffer-size" show value function. */
- static void
- show_record_bts_buffer_size_value (struct ui_file *file, int from_tty,
- struct cmd_list_element *c,
- const char *value)
- {
- gdb_printf (file, _("The record/replay bts buffer size is %s.\n"),
- value);
- }
- /* The "record pt buffer-size" show value function. */
- static void
- show_record_pt_buffer_size_value (struct ui_file *file, int from_tty,
- struct cmd_list_element *c,
- const char *value)
- {
- gdb_printf (file, _("The record/replay pt buffer size is %s.\n"),
- value);
- }
- /* Initialize btrace commands. */
- void _initialize_record_btrace ();
- void
- _initialize_record_btrace ()
- {
- cmd_list_element *record_btrace_cmd
- = add_prefix_cmd ("btrace", class_obscure, cmd_record_btrace_start,
- _("Start branch trace recording."),
- &record_btrace_cmdlist, 0, &record_cmdlist);
- add_alias_cmd ("b", record_btrace_cmd, class_obscure, 1, &record_cmdlist);
- cmd_list_element *record_btrace_bts_cmd
- = add_cmd ("bts", class_obscure, cmd_record_btrace_bts_start,
- _("\
- Start branch trace recording in Branch Trace Store (BTS) format.\n\n\
- The processor stores a from/to record for each branch into a cyclic buffer.\n\
- This format may not be available on all processors."),
- &record_btrace_cmdlist);
- add_alias_cmd ("bts", record_btrace_bts_cmd, class_obscure, 1,
- &record_cmdlist);
- cmd_list_element *record_btrace_pt_cmd
- = add_cmd ("pt", class_obscure, cmd_record_btrace_pt_start,
- _("\
- Start branch trace recording in Intel Processor Trace format.\n\n\
- This format may not be available on all processors."),
- &record_btrace_cmdlist);
- add_alias_cmd ("pt", record_btrace_pt_cmd, class_obscure, 1, &record_cmdlist);
- add_setshow_prefix_cmd ("btrace", class_support,
- _("Set record options."),
- _("Show record options."),
- &set_record_btrace_cmdlist,
- &show_record_btrace_cmdlist,
- &set_record_cmdlist, &show_record_cmdlist);
- add_setshow_enum_cmd ("replay-memory-access", no_class,
- replay_memory_access_types, &replay_memory_access, _("\
- Set what memory accesses are allowed during replay."), _("\
- Show what memory accesses are allowed during replay."),
- _("Default is READ-ONLY.\n\n\
- The btrace record target does not trace data.\n\
- The memory therefore corresponds to the live target and not \
- to the current replay position.\n\n\
- When READ-ONLY, allow accesses to read-only memory during replay.\n\
- When READ-WRITE, allow accesses to read-only and read-write memory during \
- replay."),
- NULL, cmd_show_replay_memory_access,
- &set_record_btrace_cmdlist,
- &show_record_btrace_cmdlist);
- add_prefix_cmd ("cpu", class_support, cmd_set_record_btrace_cpu,
- _("\
- Set the cpu to be used for trace decode.\n\n\
- The format is \"VENDOR:IDENTIFIER\" or \"none\" or \"auto\" (default).\n\
- For vendor \"intel\" the format is \"FAMILY/MODEL[/STEPPING]\".\n\n\
- When decoding branch trace, enable errata workarounds for the specified cpu.\n\
- The default is \"auto\", which uses the cpu on which the trace was recorded.\n\
- When GDB does not support that cpu, this option can be used to enable\n\
- workarounds for a similar cpu that GDB supports.\n\n\
- When set to \"none\", errata workarounds are disabled."),
- &set_record_btrace_cpu_cmdlist,
- 1,
- &set_record_btrace_cmdlist);
- add_cmd ("auto", class_support, cmd_set_record_btrace_cpu_auto, _("\
- Automatically determine the cpu to be used for trace decode."),
- &set_record_btrace_cpu_cmdlist);
- add_cmd ("none", class_support, cmd_set_record_btrace_cpu_none, _("\
- Do not enable errata workarounds for trace decode."),
- &set_record_btrace_cpu_cmdlist);
- add_cmd ("cpu", class_support, cmd_show_record_btrace_cpu, _("\
- Show the cpu to be used for trace decode."),
- &show_record_btrace_cmdlist);
- add_setshow_prefix_cmd ("bts", class_support,
- _("Set record btrace bts options."),
- _("Show record btrace bts options."),
- &set_record_btrace_bts_cmdlist,
- &show_record_btrace_bts_cmdlist,
- &set_record_btrace_cmdlist,
- &show_record_btrace_cmdlist);
- add_setshow_uinteger_cmd ("buffer-size", no_class,
- &record_btrace_conf.bts.size,
- _("Set the record/replay bts buffer size."),
- _("Show the record/replay bts buffer size."), _("\
- When starting recording request a trace buffer of this size. \
- The actual buffer size may differ from the requested size. \
- Use \"info record\" to see the actual buffer size.\n\n\
- Bigger buffers allow longer recording but also take more time to process \
- the recorded execution trace.\n\n\
- The trace buffer size may not be changed while recording."), NULL,
- show_record_bts_buffer_size_value,
- &set_record_btrace_bts_cmdlist,
- &show_record_btrace_bts_cmdlist);
- add_setshow_prefix_cmd ("pt", class_support,
- _("Set record btrace pt options."),
- _("Show record btrace pt options."),
- &set_record_btrace_pt_cmdlist,
- &show_record_btrace_pt_cmdlist,
- &set_record_btrace_cmdlist,
- &show_record_btrace_cmdlist);
- add_setshow_uinteger_cmd ("buffer-size", no_class,
- &record_btrace_conf.pt.size,
- _("Set the record/replay pt buffer size."),
- _("Show the record/replay pt buffer size."), _("\
- Bigger buffers allow longer recording but also take more time to process \
- the recorded execution.\n\
- The actual buffer size may differ from the requested size. Use \"info record\" \
- to see the actual buffer size."), NULL, show_record_pt_buffer_size_value,
- &set_record_btrace_pt_cmdlist,
- &show_record_btrace_pt_cmdlist);
- add_target (record_btrace_target_info, record_btrace_target_open);
- bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
- xcalloc, xfree);
- record_btrace_conf.bts.size = 64 * 1024;
- record_btrace_conf.pt.size = 16 * 1024;
- }
|