12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166 |
- /* Tracing functionality for remote targets in custom GDB protocol
- Copyright (C) 1997-2022 Free Software Foundation, Inc.
- 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 "arch-utils.h"
- #include "symtab.h"
- #include "frame.h"
- #include "gdbtypes.h"
- #include "expression.h"
- #include "gdbcmd.h"
- #include "value.h"
- #include "target.h"
- #include "target-dcache.h"
- #include "language.h"
- #include "inferior.h"
- #include "breakpoint.h"
- #include "tracepoint.h"
- #include "linespec.h"
- #include "regcache.h"
- #include "completer.h"
- #include "block.h"
- #include "dictionary.h"
- #include "observable.h"
- #include "user-regs.h"
- #include "valprint.h"
- #include "gdbcore.h"
- #include "objfiles.h"
- #include "filenames.h"
- #include "gdbthread.h"
- #include "stack.h"
- #include "remote.h"
- #include "source.h"
- #include "ax.h"
- #include "ax-gdb.h"
- #include "memrange.h"
- #include "cli/cli-utils.h"
- #include "probe.h"
- #include "gdbsupport/filestuff.h"
- #include "gdbsupport/rsp-low.h"
- #include "tracefile.h"
- #include "location.h"
- #include <algorithm>
- #include "cli/cli-style.h"
- #include "expop.h"
- #include "gdbsupport/buildargv.h"
- #include <unistd.h>
- /* Maximum length of an agent aexpression.
- This accounts for the fact that packets are limited to 400 bytes
- (which includes everything -- including the checksum), and assumes
- the worst case of maximum length for each of the pieces of a
- continuation packet.
- NOTE: expressions get mem2hex'ed otherwise this would be twice as
- large. (400 - 31)/2 == 184 */
- #define MAX_AGENT_EXPR_LEN 184
- /*
- Tracepoint.c:
- This module defines the following debugger commands:
- trace : set a tracepoint on a function, line, or address.
- info trace : list all debugger-defined tracepoints.
- delete trace : delete one or more tracepoints.
- enable trace : enable one or more tracepoints.
- disable trace : disable one or more tracepoints.
- actions : specify actions to be taken at a tracepoint.
- passcount : specify a pass count for a tracepoint.
- tstart : start a trace experiment.
- tstop : stop a trace experiment.
- tstatus : query the status of a trace experiment.
- tfind : find a trace frame in the trace buffer.
- tdump : print everything collected at the current tracepoint.
- save-tracepoints : write tracepoint setup into a file.
- This module defines the following user-visible debugger variables:
- $trace_frame : sequence number of trace frame currently being debugged.
- $trace_line : source line of trace frame currently being debugged.
- $trace_file : source file of trace frame currently being debugged.
- $tracepoint : tracepoint number of trace frame currently being debugged.
- */
- /* ======= Important global variables: ======= */
- /* The list of all trace state variables. We don't retain pointers to
- any of these for any reason - API is by name or number only - so it
- works to have a vector of objects. */
- static std::vector<trace_state_variable> tvariables;
- /* The next integer to assign to a variable. */
- static int next_tsv_number = 1;
- /* Number of last traceframe collected. */
- static int traceframe_number;
- /* Tracepoint for last traceframe collected. */
- static int tracepoint_number;
- /* The traceframe info of the current traceframe. NULL if we haven't
- yet attempted to fetch it, or if the target does not support
- fetching this object, or if we're not inspecting a traceframe
- presently. */
- static traceframe_info_up current_traceframe_info;
- /* Tracing command lists. */
- static struct cmd_list_element *tfindlist;
- /* List of expressions to collect by default at each tracepoint hit. */
- std::string default_collect;
- static bool disconnected_tracing;
- /* This variable controls whether we ask the target for a linear or
- circular trace buffer. */
- static bool circular_trace_buffer;
- /* This variable is the requested trace buffer size, or -1 to indicate
- that we don't care and leave it up to the target to set a size. */
- static int trace_buffer_size = -1;
- /* Textual notes applying to the current and/or future trace runs. */
- static std::string trace_user;
- /* Textual notes applying to the current and/or future trace runs. */
- static std::string trace_notes;
- /* Textual notes applying to the stopping of a trace. */
- static std::string trace_stop_notes;
- /* support routines */
- struct collection_list;
- static char *mem2hex (gdb_byte *, char *, int);
- static counted_command_line all_tracepoint_actions (struct breakpoint *);
- static struct trace_status trace_status;
- const char *stop_reason_names[] = {
- "tunknown",
- "tnotrun",
- "tstop",
- "tfull",
- "tdisconnected",
- "tpasscount",
- "terror"
- };
- struct trace_status *
- current_trace_status (void)
- {
- return &trace_status;
- }
- /* Free and clear the traceframe info cache of the current
- traceframe. */
- static void
- clear_traceframe_info (void)
- {
- current_traceframe_info = NULL;
- }
- /* Set traceframe number to NUM. */
- static void
- set_traceframe_num (int num)
- {
- traceframe_number = num;
- set_internalvar_integer (lookup_internalvar ("trace_frame"), num);
- }
- /* Set tracepoint number to NUM. */
- static void
- set_tracepoint_num (int num)
- {
- tracepoint_number = num;
- set_internalvar_integer (lookup_internalvar ("tracepoint"), num);
- }
- /* Set externally visible debug variables for querying/printing
- the traceframe context (line, function, file). */
- static void
- set_traceframe_context (struct frame_info *trace_frame)
- {
- CORE_ADDR trace_pc;
- struct symbol *traceframe_fun;
- symtab_and_line traceframe_sal;
- /* Save as globals for internal use. */
- if (trace_frame != NULL
- && get_frame_pc_if_available (trace_frame, &trace_pc))
- {
- traceframe_sal = find_pc_line (trace_pc, 0);
- traceframe_fun = find_pc_function (trace_pc);
- /* Save linenumber as "$trace_line", a debugger variable visible to
- users. */
- set_internalvar_integer (lookup_internalvar ("trace_line"),
- traceframe_sal.line);
- }
- else
- {
- traceframe_fun = NULL;
- set_internalvar_integer (lookup_internalvar ("trace_line"), -1);
- }
- /* Save func name as "$trace_func", a debugger variable visible to
- users. */
- if (traceframe_fun == NULL
- || traceframe_fun->linkage_name () == NULL)
- clear_internalvar (lookup_internalvar ("trace_func"));
- else
- set_internalvar_string (lookup_internalvar ("trace_func"),
- traceframe_fun->linkage_name ());
- /* Save file name as "$trace_file", a debugger variable visible to
- users. */
- if (traceframe_sal.symtab == NULL)
- clear_internalvar (lookup_internalvar ("trace_file"));
- else
- set_internalvar_string (lookup_internalvar ("trace_file"),
- symtab_to_filename_for_display (traceframe_sal.symtab));
- }
- /* Create a new trace state variable with the given name. */
- struct trace_state_variable *
- create_trace_state_variable (const char *name)
- {
- tvariables.emplace_back (name, next_tsv_number++);
- return &tvariables.back ();
- }
- /* Look for a trace state variable of the given name. */
- struct trace_state_variable *
- find_trace_state_variable (const char *name)
- {
- for (trace_state_variable &tsv : tvariables)
- if (tsv.name == name)
- return &tsv;
- return NULL;
- }
- /* Look for a trace state variable of the given number. Return NULL if
- not found. */
- struct trace_state_variable *
- find_trace_state_variable_by_number (int number)
- {
- for (trace_state_variable &tsv : tvariables)
- if (tsv.number == number)
- return &tsv;
- return NULL;
- }
- static void
- delete_trace_state_variable (const char *name)
- {
- for (auto it = tvariables.begin (); it != tvariables.end (); it++)
- if (it->name == name)
- {
- gdb::observers::tsv_deleted.notify (&*it);
- tvariables.erase (it);
- return;
- }
- warning (_("No trace variable named \"$%s\", not deleting"), name);
- }
- /* Throws an error if NAME is not valid syntax for a trace state
- variable's name. */
- void
- validate_trace_state_variable_name (const char *name)
- {
- const char *p;
- if (*name == '\0')
- error (_("Must supply a non-empty variable name"));
- /* All digits in the name is reserved for value history
- references. */
- for (p = name; isdigit (*p); p++)
- ;
- if (*p == '\0')
- error (_("$%s is not a valid trace state variable name"), name);
- for (p = name; isalnum (*p) || *p == '_'; p++)
- ;
- if (*p != '\0')
- error (_("$%s is not a valid trace state variable name"), name);
- }
- /* The 'tvariable' command collects a name and optional expression to
- evaluate into an initial value. */
- static void
- trace_variable_command (const char *args, int from_tty)
- {
- LONGEST initval = 0;
- struct trace_state_variable *tsv;
- const char *name_start, *p;
- if (!args || !*args)
- error_no_arg (_("Syntax is $NAME [ = EXPR ]"));
- /* Only allow two syntaxes; "$name" and "$name=value". */
- p = skip_spaces (args);
- if (*p++ != '$')
- error (_("Name of trace variable should start with '$'"));
- name_start = p;
- while (isalnum (*p) || *p == '_')
- p++;
- std::string name (name_start, p - name_start);
- p = skip_spaces (p);
- if (*p != '=' && *p != '\0')
- error (_("Syntax must be $NAME [ = EXPR ]"));
- validate_trace_state_variable_name (name.c_str ());
- if (*p == '=')
- initval = value_as_long (parse_and_eval (++p));
- /* If the variable already exists, just change its initial value. */
- tsv = find_trace_state_variable (name.c_str ());
- if (tsv)
- {
- if (tsv->initial_value != initval)
- {
- tsv->initial_value = initval;
- gdb::observers::tsv_modified.notify (tsv);
- }
- gdb_printf (_("Trace state variable $%s "
- "now has initial value %s.\n"),
- tsv->name.c_str (), plongest (tsv->initial_value));
- return;
- }
- /* Create a new variable. */
- tsv = create_trace_state_variable (name.c_str ());
- tsv->initial_value = initval;
- gdb::observers::tsv_created.notify (tsv);
- gdb_printf (_("Trace state variable $%s "
- "created, with initial value %s.\n"),
- tsv->name.c_str (), plongest (tsv->initial_value));
- }
- static void
- delete_trace_variable_command (const char *args, int from_tty)
- {
- if (args == NULL)
- {
- if (query (_("Delete all trace state variables? ")))
- tvariables.clear ();
- dont_repeat ();
- gdb::observers::tsv_deleted.notify (NULL);
- return;
- }
- gdb_argv argv (args);
- for (char *arg : argv)
- {
- if (*arg == '$')
- delete_trace_state_variable (arg + 1);
- else
- warning (_("Name \"%s\" not prefixed with '$', ignoring"), arg);
- }
- dont_repeat ();
- }
- void
- tvariables_info_1 (void)
- {
- struct ui_out *uiout = current_uiout;
- /* Try to acquire values from the target. */
- for (trace_state_variable &tsv : tvariables)
- tsv.value_known
- = target_get_trace_state_variable_value (tsv.number, &tsv.value);
- {
- ui_out_emit_table table_emitter (uiout, 3, tvariables.size (),
- "trace-variables");
- uiout->table_header (15, ui_left, "name", "Name");
- uiout->table_header (11, ui_left, "initial", "Initial");
- uiout->table_header (11, ui_left, "current", "Current");
- uiout->table_body ();
- for (const trace_state_variable &tsv : tvariables)
- {
- const char *c;
- ui_out_emit_tuple tuple_emitter (uiout, "variable");
- uiout->field_string ("name", std::string ("$") + tsv.name);
- uiout->field_string ("initial", plongest (tsv.initial_value));
- ui_file_style style;
- if (tsv.value_known)
- c = plongest (tsv.value);
- else if (uiout->is_mi_like_p ())
- /* For MI, we prefer not to use magic string constants, but rather
- omit the field completely. The difference between unknown and
- undefined does not seem important enough to represent. */
- c = NULL;
- else if (current_trace_status ()->running || traceframe_number >= 0)
- {
- /* The value is/was defined, but we don't have it. */
- c = "<unknown>";
- style = metadata_style.style ();
- }
- else
- {
- /* It is not meaningful to ask about the value. */
- c = "<undefined>";
- style = metadata_style.style ();
- }
- if (c)
- uiout->field_string ("current", c, style);
- uiout->text ("\n");
- }
- }
- if (tvariables.empty ())
- uiout->text (_("No trace state variables.\n"));
- }
- /* List all the trace state variables. */
- static void
- info_tvariables_command (const char *args, int from_tty)
- {
- tvariables_info_1 ();
- }
- /* Stash definitions of tsvs into the given file. */
- void
- save_trace_state_variables (struct ui_file *fp)
- {
- for (const trace_state_variable &tsv : tvariables)
- {
- gdb_printf (fp, "tvariable $%s", tsv.name.c_str ());
- if (tsv.initial_value)
- gdb_printf (fp, " = %s", plongest (tsv.initial_value));
- gdb_printf (fp, "\n");
- }
- }
- /* ACTIONS functions: */
- /* The three functions:
- collect_pseudocommand,
- while_stepping_pseudocommand, and
- end_actions_pseudocommand
- are placeholders for "commands" that are actually ONLY to be used
- within a tracepoint action list. If the actual function is ever called,
- it means that somebody issued the "command" at the top level,
- which is always an error. */
- static void
- end_actions_pseudocommand (const char *args, int from_tty)
- {
- error (_("This command cannot be used at the top level."));
- }
- static void
- while_stepping_pseudocommand (const char *args, int from_tty)
- {
- error (_("This command can only be used in a tracepoint actions list."));
- }
- static void
- collect_pseudocommand (const char *args, int from_tty)
- {
- error (_("This command can only be used in a tracepoint actions list."));
- }
- static void
- teval_pseudocommand (const char *args, int from_tty)
- {
- error (_("This command can only be used in a tracepoint actions list."));
- }
- /* Parse any collection options, such as /s for strings. */
- const char *
- decode_agent_options (const char *exp, int *trace_string)
- {
- struct value_print_options opts;
- *trace_string = 0;
- if (*exp != '/')
- return exp;
- /* Call this to borrow the print elements default for collection
- size. */
- get_user_print_options (&opts);
- exp++;
- if (*exp == 's')
- {
- if (target_supports_string_tracing ())
- {
- /* Allow an optional decimal number giving an explicit maximum
- string length, defaulting it to the "print elements" value;
- so "collect/s80 mystr" gets at most 80 bytes of string. */
- *trace_string = opts.print_max;
- exp++;
- if (*exp >= '0' && *exp <= '9')
- *trace_string = atoi (exp);
- while (*exp >= '0' && *exp <= '9')
- exp++;
- }
- else
- error (_("Target does not support \"/s\" option for string tracing."));
- }
- else
- error (_("Undefined collection format \"%c\"."), *exp);
- exp = skip_spaces (exp);
- return exp;
- }
- /* Enter a list of actions for a tracepoint. */
- static void
- actions_command (const char *args, int from_tty)
- {
- struct tracepoint *t;
- t = get_tracepoint_by_number (&args, NULL);
- if (t)
- {
- std::string tmpbuf =
- string_printf ("Enter actions for tracepoint %d, one per line.",
- t->number);
- counted_command_line l = read_command_lines (tmpbuf.c_str (),
- from_tty, 1,
- [=] (const char *line)
- {
- validate_actionline (line, t);
- });
- breakpoint_set_commands (t, std::move (l));
- }
- /* else just return */
- }
- /* Report the results of checking the agent expression, as errors or
- internal errors. */
- static void
- report_agent_reqs_errors (struct agent_expr *aexpr)
- {
- /* All of the "flaws" are serious bytecode generation issues that
- should never occur. */
- if (aexpr->flaw != agent_flaw_none)
- internal_error (__FILE__, __LINE__, _("expression is malformed"));
- /* If analysis shows a stack underflow, GDB must have done something
- badly wrong in its bytecode generation. */
- if (aexpr->min_height < 0)
- internal_error (__FILE__, __LINE__,
- _("expression has min height < 0"));
- /* Issue this error if the stack is predicted to get too deep. The
- limit is rather arbitrary; a better scheme might be for the
- target to report how much stack it will have available. The
- depth roughly corresponds to parenthesization, so a limit of 20
- amounts to 20 levels of expression nesting, which is actually
- a pretty big hairy expression. */
- if (aexpr->max_height > 20)
- error (_("Expression is too complicated."));
- }
- /* Call ax_reqs on AEXPR and raise an error if something is wrong. */
- static void
- finalize_tracepoint_aexpr (struct agent_expr *aexpr)
- {
- ax_reqs (aexpr);
- if (aexpr->len > MAX_AGENT_EXPR_LEN)
- error (_("Expression is too complicated."));
- report_agent_reqs_errors (aexpr);
- }
- /* worker function */
- void
- validate_actionline (const char *line, struct breakpoint *b)
- {
- struct cmd_list_element *c;
- const char *tmp_p;
- const char *p;
- struct tracepoint *t = (struct tracepoint *) b;
- /* If EOF is typed, *line is NULL. */
- if (line == NULL)
- return;
- p = skip_spaces (line);
- /* Symbol lookup etc. */
- if (*p == '\0') /* empty line: just prompt for another line. */
- return;
- if (*p == '#') /* comment line */
- return;
- c = lookup_cmd (&p, cmdlist, "", NULL, -1, 1);
- if (c == 0)
- error (_("`%s' is not a tracepoint action, or is ambiguous."), p);
- if (cmd_simple_func_eq (c, collect_pseudocommand))
- {
- int trace_string = 0;
- if (*p == '/')
- p = decode_agent_options (p, &trace_string);
- do
- { /* Repeat over a comma-separated list. */
- QUIT; /* Allow user to bail out with ^C. */
- p = skip_spaces (p);
- if (*p == '$') /* Look for special pseudo-symbols. */
- {
- if (0 == strncasecmp ("reg", p + 1, 3)
- || 0 == strncasecmp ("arg", p + 1, 3)
- || 0 == strncasecmp ("loc", p + 1, 3)
- || 0 == strncasecmp ("_ret", p + 1, 4)
- || 0 == strncasecmp ("_sdata", p + 1, 6))
- {
- p = strchr (p, ',');
- continue;
- }
- /* else fall thru, treat p as an expression and parse it! */
- }
- tmp_p = p;
- for (bp_location *loc : t->locations ())
- {
- p = tmp_p;
- expression_up exp = parse_exp_1 (&p, loc->address,
- block_for_pc (loc->address), 1);
- if (exp->first_opcode () == OP_VAR_VALUE)
- {
- symbol *sym;
- expr::var_value_operation *vvop
- = (dynamic_cast<expr::var_value_operation *>
- (exp->op.get ()));
- sym = vvop->get_symbol ();
- if (sym->aclass () == LOC_CONST)
- {
- error (_("constant `%s' (value %s) "
- "will not be collected."),
- sym->print_name (),
- plongest (SYMBOL_VALUE (sym)));
- }
- else if (sym->aclass () == LOC_OPTIMIZED_OUT)
- {
- error (_("`%s' is optimized away "
- "and cannot be collected."),
- sym->print_name ());
- }
- }
- /* We have something to collect, make sure that the expr to
- bytecode translator can handle it and that it's not too
- long. */
- agent_expr_up aexpr = gen_trace_for_expr (loc->address,
- exp.get (),
- trace_string);
- finalize_tracepoint_aexpr (aexpr.get ());
- }
- }
- while (p && *p++ == ',');
- }
- else if (cmd_simple_func_eq (c, teval_pseudocommand))
- {
- do
- { /* Repeat over a comma-separated list. */
- QUIT; /* Allow user to bail out with ^C. */
- p = skip_spaces (p);
- tmp_p = p;
- for (bp_location *loc : t->locations ())
- {
- p = tmp_p;
- /* Only expressions are allowed for this action. */
- expression_up exp = parse_exp_1 (&p, loc->address,
- block_for_pc (loc->address), 1);
- /* We have something to evaluate, make sure that the expr to
- bytecode translator can handle it and that it's not too
- long. */
- agent_expr_up aexpr = gen_eval_for_expr (loc->address, exp.get ());
- finalize_tracepoint_aexpr (aexpr.get ());
- }
- }
- while (p && *p++ == ',');
- }
- else if (cmd_simple_func_eq (c, while_stepping_pseudocommand))
- {
- char *endp;
- p = skip_spaces (p);
- t->step_count = strtol (p, &endp, 0);
- if (endp == p || t->step_count == 0)
- error (_("while-stepping step count `%s' is malformed."), line);
- p = endp;
- }
- else if (cmd_simple_func_eq (c, end_actions_pseudocommand))
- ;
- else
- error (_("`%s' is not a supported tracepoint action."), line);
- }
- enum {
- memrange_absolute = -1
- };
- /* MEMRANGE functions: */
- /* Compare memranges for std::sort. */
- static bool
- memrange_comp (const memrange &a, const memrange &b)
- {
- if (a.type == b.type)
- {
- if (a.type == memrange_absolute)
- return (bfd_vma) a.start < (bfd_vma) b.start;
- else
- return a.start < b.start;
- }
- return a.type < b.type;
- }
- /* Sort the memrange list using std::sort, and merge adjacent memranges. */
- static void
- memrange_sortmerge (std::vector<memrange> &memranges)
- {
- if (!memranges.empty ())
- {
- int a, b;
- std::sort (memranges.begin (), memranges.end (), memrange_comp);
- for (a = 0, b = 1; b < memranges.size (); b++)
- {
- /* If memrange b overlaps or is adjacent to memrange a,
- merge them. */
- if (memranges[a].type == memranges[b].type
- && memranges[b].start <= memranges[a].end)
- {
- if (memranges[b].end > memranges[a].end)
- memranges[a].end = memranges[b].end;
- continue; /* next b, same a */
- }
- a++; /* next a */
- if (a != b)
- memranges[a] = memranges[b];
- }
- memranges.resize (a + 1);
- }
- }
- /* Add remote register number REGNO to the collection list mask. */
- void
- collection_list::add_remote_register (unsigned int regno)
- {
- if (info_verbose)
- gdb_printf ("collect register %d\n", regno);
- m_regs_mask.at (regno / 8) |= 1 << (regno % 8);
- }
- /* Add all the registers from the mask in AEXPR to the mask in the
- collection list. Registers in the AEXPR mask are already remote
- register numbers. */
- void
- collection_list::add_ax_registers (struct agent_expr *aexpr)
- {
- if (aexpr->reg_mask_len > 0)
- {
- for (int ndx1 = 0; ndx1 < aexpr->reg_mask_len; ndx1++)
- {
- QUIT; /* Allow user to bail out with ^C. */
- if (aexpr->reg_mask[ndx1] != 0)
- {
- /* Assume chars have 8 bits. */
- for (int ndx2 = 0; ndx2 < 8; ndx2++)
- if (aexpr->reg_mask[ndx1] & (1 << ndx2))
- /* It's used -- record it. */
- add_remote_register (ndx1 * 8 + ndx2);
- }
- }
- }
- }
- /* If REGNO is raw, add its corresponding remote register number to
- the mask. If REGNO is a pseudo-register, figure out the necessary
- registers using a temporary agent expression, and add it to the
- list if it needs more than just a mask. */
- void
- collection_list::add_local_register (struct gdbarch *gdbarch,
- unsigned int regno,
- CORE_ADDR scope)
- {
- if (regno < gdbarch_num_regs (gdbarch))
- {
- int remote_regno = gdbarch_remote_register_number (gdbarch, regno);
- if (remote_regno < 0)
- error (_("Can't collect register %d"), regno);
- add_remote_register (remote_regno);
- }
- else
- {
- agent_expr_up aexpr (new agent_expr (gdbarch, scope));
- ax_reg_mask (aexpr.get (), regno);
- finalize_tracepoint_aexpr (aexpr.get ());
- add_ax_registers (aexpr.get ());
- /* Usually ax_reg_mask for a pseudo-regiser only sets the
- corresponding raw registers in the ax mask, but if this isn't
- the case add the expression that is generated to the
- collection list. */
- if (aexpr->len > 0)
- add_aexpr (std::move (aexpr));
- }
- }
- /* Add a memrange to a collection list. */
- void
- collection_list::add_memrange (struct gdbarch *gdbarch,
- int type, bfd_signed_vma base,
- unsigned long len, CORE_ADDR scope)
- {
- if (info_verbose)
- gdb_printf ("(%d,%s,%ld)\n", type, paddress (gdbarch, base), len);
- /* type: memrange_absolute == memory, other n == basereg */
- /* base: addr if memory, offset if reg relative. */
- /* len: we actually save end (base + len) for convenience */
- m_memranges.emplace_back (type, base, base + len);
- if (type != memrange_absolute) /* Better collect the base register! */
- add_local_register (gdbarch, type, scope);
- }
- /* Add a symbol to a collection list. */
- void
- collection_list::collect_symbol (struct symbol *sym,
- struct gdbarch *gdbarch,
- long frame_regno, long frame_offset,
- CORE_ADDR scope,
- int trace_string)
- {
- unsigned long len;
- unsigned int reg;
- bfd_signed_vma offset;
- int treat_as_expr = 0;
- len = TYPE_LENGTH (check_typedef (sym->type ()));
- switch (sym->aclass ())
- {
- default:
- gdb_printf ("%s: don't know symbol class %d\n",
- sym->print_name (), sym->aclass ());
- break;
- case LOC_CONST:
- gdb_printf ("constant %s (value %s) will not be collected.\n",
- sym->print_name (), plongest (SYMBOL_VALUE (sym)));
- break;
- case LOC_STATIC:
- offset = SYMBOL_VALUE_ADDRESS (sym);
- if (info_verbose)
- {
- gdb_printf ("LOC_STATIC %s: collect %ld bytes at %s.\n",
- sym->print_name (), len,
- paddress (gdbarch, offset));
- }
- /* A struct may be a C++ class with static fields, go to general
- expression handling. */
- if (sym->type ()->code () == TYPE_CODE_STRUCT)
- treat_as_expr = 1;
- else
- add_memrange (gdbarch, memrange_absolute, offset, len, scope);
- break;
- case LOC_REGISTER:
- reg = SYMBOL_REGISTER_OPS (sym)->register_number (sym, gdbarch);
- if (info_verbose)
- gdb_printf ("LOC_REG[parm] %s: ", sym->print_name ());
- add_local_register (gdbarch, reg, scope);
- /* Check for doubles stored in two registers. */
- /* FIXME: how about larger types stored in 3 or more regs? */
- if (sym->type ()->code () == TYPE_CODE_FLT &&
- len > register_size (gdbarch, reg))
- add_local_register (gdbarch, reg + 1, scope);
- break;
- case LOC_REF_ARG:
- gdb_printf ("Sorry, don't know how to do LOC_REF_ARG yet.\n");
- gdb_printf (" (will not collect %s)\n", sym->print_name ());
- break;
- case LOC_ARG:
- reg = frame_regno;
- offset = frame_offset + SYMBOL_VALUE (sym);
- if (info_verbose)
- {
- gdb_printf ("LOC_LOCAL %s: Collect %ld bytes at offset %s"
- " from frame ptr reg %d\n", sym->print_name (), len,
- paddress (gdbarch, offset), reg);
- }
- add_memrange (gdbarch, reg, offset, len, scope);
- break;
- case LOC_REGPARM_ADDR:
- reg = SYMBOL_VALUE (sym);
- offset = 0;
- if (info_verbose)
- {
- gdb_printf ("LOC_REGPARM_ADDR %s: Collect %ld bytes at offset %s"
- " from reg %d\n", sym->print_name (), len,
- paddress (gdbarch, offset), reg);
- }
- add_memrange (gdbarch, reg, offset, len, scope);
- break;
- case LOC_LOCAL:
- reg = frame_regno;
- offset = frame_offset + SYMBOL_VALUE (sym);
- if (info_verbose)
- {
- gdb_printf ("LOC_LOCAL %s: Collect %ld bytes at offset %s"
- " from frame ptr reg %d\n", sym->print_name (), len,
- paddress (gdbarch, offset), reg);
- }
- add_memrange (gdbarch, reg, offset, len, scope);
- break;
- case LOC_UNRESOLVED:
- treat_as_expr = 1;
- break;
- case LOC_OPTIMIZED_OUT:
- gdb_printf ("%s has been optimized out of existence.\n",
- sym->print_name ());
- break;
- case LOC_COMPUTED:
- treat_as_expr = 1;
- break;
- }
- /* Expressions are the most general case. */
- if (treat_as_expr)
- {
- agent_expr_up aexpr = gen_trace_for_var (scope, gdbarch,
- sym, trace_string);
- /* It can happen that the symbol is recorded as a computed
- location, but it's been optimized away and doesn't actually
- have a location expression. */
- if (!aexpr)
- {
- gdb_printf ("%s has been optimized out of existence.\n",
- sym->print_name ());
- return;
- }
- finalize_tracepoint_aexpr (aexpr.get ());
- /* Take care of the registers. */
- add_ax_registers (aexpr.get ());
- add_aexpr (std::move (aexpr));
- }
- }
- void
- collection_list::add_wholly_collected (const char *print_name)
- {
- m_wholly_collected.push_back (print_name);
- }
- /* Add all locals (or args) symbols to collection list. */
- void
- collection_list::add_local_symbols (struct gdbarch *gdbarch, CORE_ADDR pc,
- long frame_regno, long frame_offset, int type,
- int trace_string)
- {
- const struct block *block;
- int count = 0;
- auto do_collect_symbol = [&] (const char *print_name,
- struct symbol *sym)
- {
- collect_symbol (sym, gdbarch, frame_regno,
- frame_offset, pc, trace_string);
- count++;
- add_wholly_collected (print_name);
- };
- if (type == 'L')
- {
- block = block_for_pc (pc);
- if (block == NULL)
- {
- warning (_("Can't collect locals; "
- "no symbol table info available.\n"));
- return;
- }
- iterate_over_block_local_vars (block, do_collect_symbol);
- if (count == 0)
- warning (_("No locals found in scope."));
- }
- else
- {
- CORE_ADDR fn_pc = get_pc_function_start (pc);
- block = block_for_pc (fn_pc);
- if (block == NULL)
- {
- warning (_("Can't collect args; no symbol table info available."));
- return;
- }
- iterate_over_block_arg_vars (block, do_collect_symbol);
- if (count == 0)
- warning (_("No args found in scope."));
- }
- }
- void
- collection_list::add_static_trace_data ()
- {
- if (info_verbose)
- gdb_printf ("collect static trace data\n");
- m_strace_data = true;
- }
- collection_list::collection_list ()
- : m_strace_data (false)
- {
- int max_remote_regno = 0;
- for (int i = 0; i < gdbarch_num_regs (target_gdbarch ()); i++)
- {
- int remote_regno = (gdbarch_remote_register_number
- (target_gdbarch (), i));
- if (remote_regno >= 0 && remote_regno > max_remote_regno)
- max_remote_regno = remote_regno;
- }
- m_regs_mask.resize ((max_remote_regno / 8) + 1);
- m_memranges.reserve (128);
- m_aexprs.reserve (128);
- }
- /* Reduce a collection list to string form (for gdb protocol). */
- std::vector<std::string>
- collection_list::stringify ()
- {
- gdb::char_vector temp_buf (2048);
- int count;
- char *end;
- long i;
- std::vector<std::string> str_list;
- if (m_strace_data)
- {
- if (info_verbose)
- gdb_printf ("\nCollecting static trace data\n");
- end = temp_buf.data ();
- *end++ = 'L';
- str_list.emplace_back (temp_buf.data (), end - temp_buf.data ());
- }
- for (i = m_regs_mask.size () - 1; i > 0; i--)
- if (m_regs_mask[i] != 0) /* Skip leading zeroes in regs_mask. */
- break;
- if (m_regs_mask[i] != 0) /* Prepare to send regs_mask to the stub. */
- {
- if (info_verbose)
- gdb_printf ("\nCollecting registers (mask): 0x");
- /* One char for 'R', one for the null terminator and two per
- mask byte. */
- std::size_t new_size = (i + 1) * 2 + 2;
- if (new_size > temp_buf.size ())
- temp_buf.resize (new_size);
- end = temp_buf.data ();
- *end++ = 'R';
- for (; i >= 0; i--)
- {
- QUIT; /* Allow user to bail out with ^C. */
- if (info_verbose)
- gdb_printf ("%02X", m_regs_mask[i]);
- end = pack_hex_byte (end, m_regs_mask[i]);
- }
- *end = '\0';
- str_list.emplace_back (temp_buf.data ());
- }
- if (info_verbose)
- gdb_printf ("\n");
- if (!m_memranges.empty () && info_verbose)
- gdb_printf ("Collecting memranges: \n");
- for (i = 0, count = 0, end = temp_buf.data ();
- i < m_memranges.size (); i++)
- {
- QUIT; /* Allow user to bail out with ^C. */
- if (info_verbose)
- {
- gdb_printf ("(%d, %s, %ld)\n",
- m_memranges[i].type,
- paddress (target_gdbarch (),
- m_memranges[i].start),
- (long) (m_memranges[i].end
- - m_memranges[i].start));
- }
- if (count + 27 > MAX_AGENT_EXPR_LEN)
- {
- str_list.emplace_back (temp_buf.data (), count);
- count = 0;
- end = temp_buf.data ();
- }
- {
- bfd_signed_vma length
- = m_memranges[i].end - m_memranges[i].start;
- /* The "%X" conversion specifier expects an unsigned argument,
- so passing -1 (memrange_absolute) to it directly gives you
- "FFFFFFFF" (or more, depending on sizeof (unsigned)).
- Special-case it. */
- if (m_memranges[i].type == memrange_absolute)
- sprintf (end, "M-1,%s,%lX", phex_nz (m_memranges[i].start, 0),
- (long) length);
- else
- sprintf (end, "M%X,%s,%lX", m_memranges[i].type,
- phex_nz (m_memranges[i].start, 0), (long) length);
- }
- count += strlen (end);
- end = temp_buf.data () + count;
- }
- for (i = 0; i < m_aexprs.size (); i++)
- {
- QUIT; /* Allow user to bail out with ^C. */
- if ((count + 10 + 2 * m_aexprs[i]->len) > MAX_AGENT_EXPR_LEN)
- {
- str_list.emplace_back (temp_buf.data (), count);
- count = 0;
- end = temp_buf.data ();
- }
- sprintf (end, "X%08X,", m_aexprs[i]->len);
- end += 10; /* 'X' + 8 hex digits + ',' */
- count += 10;
- end = mem2hex (m_aexprs[i]->buf, end, m_aexprs[i]->len);
- count += 2 * m_aexprs[i]->len;
- }
- if (count != 0)
- {
- str_list.emplace_back (temp_buf.data (), count);
- count = 0;
- end = temp_buf.data ();
- }
- return str_list;
- }
- /* Add the expression STR to M_COMPUTED. */
- void
- collection_list::append_exp (std::string &&str)
- {
- m_computed.push_back (std::move (str));
- }
- void
- collection_list::finish ()
- {
- memrange_sortmerge (m_memranges);
- }
- static void
- encode_actions_1 (struct command_line *action,
- struct bp_location *tloc,
- int frame_reg,
- LONGEST frame_offset,
- struct collection_list *collect,
- struct collection_list *stepping_list)
- {
- const char *action_exp;
- int i;
- struct value *tempval;
- struct cmd_list_element *cmd;
- for (; action; action = action->next)
- {
- QUIT; /* Allow user to bail out with ^C. */
- action_exp = action->line;
- action_exp = skip_spaces (action_exp);
- cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1);
- if (cmd == 0)
- error (_("Bad action list item: %s"), action_exp);
- if (cmd_simple_func_eq (cmd, collect_pseudocommand))
- {
- int trace_string = 0;
- if (*action_exp == '/')
- action_exp = decode_agent_options (action_exp, &trace_string);
- do
- { /* Repeat over a comma-separated list. */
- QUIT; /* Allow user to bail out with ^C. */
- action_exp = skip_spaces (action_exp);
- if (0 == strncasecmp ("$reg", action_exp, 4))
- {
- for (i = 0; i < gdbarch_num_regs (target_gdbarch ());
- i++)
- {
- int remote_regno = (gdbarch_remote_register_number
- (target_gdbarch (), i));
- /* Ignore arch regnos without a corresponding
- remote regno. This can happen for regnos not
- in the tdesc. */
- if (remote_regno >= 0)
- collect->add_remote_register (remote_regno);
- }
- action_exp = strchr (action_exp, ','); /* more? */
- }
- else if (0 == strncasecmp ("$arg", action_exp, 4))
- {
- collect->add_local_symbols (target_gdbarch (),
- tloc->address,
- frame_reg,
- frame_offset,
- 'A',
- trace_string);
- action_exp = strchr (action_exp, ','); /* more? */
- }
- else if (0 == strncasecmp ("$loc", action_exp, 4))
- {
- collect->add_local_symbols (target_gdbarch (),
- tloc->address,
- frame_reg,
- frame_offset,
- 'L',
- trace_string);
- action_exp = strchr (action_exp, ','); /* more? */
- }
- else if (0 == strncasecmp ("$_ret", action_exp, 5))
- {
- agent_expr_up aexpr
- = gen_trace_for_return_address (tloc->address,
- target_gdbarch (),
- trace_string);
- finalize_tracepoint_aexpr (aexpr.get ());
- /* take care of the registers */
- collect->add_ax_registers (aexpr.get ());
- collect->add_aexpr (std::move (aexpr));
- action_exp = strchr (action_exp, ','); /* more? */
- }
- else if (0 == strncasecmp ("$_sdata", action_exp, 7))
- {
- collect->add_static_trace_data ();
- action_exp = strchr (action_exp, ','); /* more? */
- }
- else
- {
- unsigned long addr;
- const char *exp_start = action_exp;
- expression_up exp = parse_exp_1 (&action_exp, tloc->address,
- block_for_pc (tloc->address),
- 1);
- switch (exp->first_opcode ())
- {
- case OP_REGISTER:
- {
- expr::register_operation *regop
- = (dynamic_cast<expr::register_operation *>
- (exp->op.get ()));
- const char *name = regop->get_name ();
- i = user_reg_map_name_to_regnum (target_gdbarch (),
- name, strlen (name));
- if (i == -1)
- internal_error (__FILE__, __LINE__,
- _("Register $%s not available"),
- name);
- if (info_verbose)
- gdb_printf ("OP_REGISTER: ");
- collect->add_local_register (target_gdbarch (),
- i, tloc->address);
- break;
- }
- case UNOP_MEMVAL:
- {
- /* Safe because we know it's a simple expression. */
- tempval = evaluate_expression (exp.get ());
- addr = value_address (tempval);
- expr::unop_memval_operation *memop
- = (dynamic_cast<expr::unop_memval_operation *>
- (exp->op.get ()));
- struct type *type = memop->get_type ();
- /* Initialize the TYPE_LENGTH if it is a typedef. */
- check_typedef (type);
- collect->add_memrange (target_gdbarch (),
- memrange_absolute, addr,
- TYPE_LENGTH (type),
- tloc->address);
- collect->append_exp (std::string (exp_start,
- action_exp));
- }
- break;
- case OP_VAR_VALUE:
- {
- expr::var_value_operation *vvo
- = (dynamic_cast<expr::var_value_operation *>
- (exp->op.get ()));
- struct symbol *sym = vvo->get_symbol ();
- const char *name = sym->natural_name ();
- collect->collect_symbol (sym,
- target_gdbarch (),
- frame_reg,
- frame_offset,
- tloc->address,
- trace_string);
- collect->add_wholly_collected (name);
- }
- break;
- default: /* Full-fledged expression. */
- agent_expr_up aexpr = gen_trace_for_expr (tloc->address,
- exp.get (),
- trace_string);
- finalize_tracepoint_aexpr (aexpr.get ());
- /* Take care of the registers. */
- collect->add_ax_registers (aexpr.get ());
- collect->add_aexpr (std::move (aexpr));
- collect->append_exp (std::string (exp_start,
- action_exp));
- break;
- } /* switch */
- } /* do */
- }
- while (action_exp && *action_exp++ == ',');
- } /* if */
- else if (cmd_simple_func_eq (cmd, teval_pseudocommand))
- {
- do
- { /* Repeat over a comma-separated list. */
- QUIT; /* Allow user to bail out with ^C. */
- action_exp = skip_spaces (action_exp);
- {
- expression_up exp = parse_exp_1 (&action_exp, tloc->address,
- block_for_pc (tloc->address),
- 1);
- agent_expr_up aexpr = gen_eval_for_expr (tloc->address,
- exp.get ());
- finalize_tracepoint_aexpr (aexpr.get ());
- /* Even though we're not officially collecting, add
- to the collect list anyway. */
- collect->add_aexpr (std::move (aexpr));
- } /* do */
- }
- while (action_exp && *action_exp++ == ',');
- } /* if */
- else if (cmd_simple_func_eq (cmd, while_stepping_pseudocommand))
- {
- /* We check against nested while-stepping when setting
- breakpoint action, so no way to run into nested
- here. */
- gdb_assert (stepping_list);
- encode_actions_1 (action->body_list_0.get (), tloc, frame_reg,
- frame_offset, stepping_list, NULL);
- }
- else
- error (_("Invalid tracepoint command '%s'"), action->line);
- } /* for */
- }
- /* Encode actions of tracepoint TLOC->owner and fill TRACEPOINT_LIST
- and STEPPING_LIST. */
- void
- encode_actions (struct bp_location *tloc,
- struct collection_list *tracepoint_list,
- struct collection_list *stepping_list)
- {
- int frame_reg;
- LONGEST frame_offset;
- gdbarch_virtual_frame_pointer (tloc->gdbarch,
- tloc->address, &frame_reg, &frame_offset);
- counted_command_line actions = all_tracepoint_actions (tloc->owner);
- encode_actions_1 (actions.get (), tloc, frame_reg, frame_offset,
- tracepoint_list, stepping_list);
- encode_actions_1 (breakpoint_commands (tloc->owner), tloc,
- frame_reg, frame_offset, tracepoint_list, stepping_list);
- tracepoint_list->finish ();
- stepping_list->finish ();
- }
- /* Render all actions into gdb protocol. */
- void
- encode_actions_rsp (struct bp_location *tloc,
- std::vector<std::string> *tdp_actions,
- std::vector<std::string> *stepping_actions)
- {
- struct collection_list tracepoint_list, stepping_list;
- encode_actions (tloc, &tracepoint_list, &stepping_list);
- *tdp_actions = tracepoint_list.stringify ();
- *stepping_actions = stepping_list.stringify ();
- }
- void
- collection_list::add_aexpr (agent_expr_up aexpr)
- {
- m_aexprs.push_back (std::move (aexpr));
- }
- static void
- process_tracepoint_on_disconnect (void)
- {
- int has_pending_p = 0;
- /* Check whether we still have pending tracepoint. If we have, warn the
- user that pending tracepoint will no longer work. */
- for (breakpoint *b : all_tracepoints ())
- {
- if (b->loc == NULL)
- {
- has_pending_p = 1;
- break;
- }
- else
- {
- for (bp_location *loc1 : b->locations ())
- {
- if (loc1->shlib_disabled)
- {
- has_pending_p = 1;
- break;
- }
- }
- if (has_pending_p)
- break;
- }
- }
- if (has_pending_p)
- warning (_("Pending tracepoints will not be resolved while"
- " GDB is disconnected\n"));
- }
- /* Reset local state of tracing. */
- void
- trace_reset_local_state (void)
- {
- set_traceframe_num (-1);
- set_tracepoint_num (-1);
- set_traceframe_context (NULL);
- clear_traceframe_info ();
- }
- void
- start_tracing (const char *notes)
- {
- int any_enabled = 0, num_to_download = 0;
- int ret;
- auto tracepoint_range = all_tracepoints ();
- /* No point in tracing without any tracepoints... */
- if (tracepoint_range.begin () == tracepoint_range.end ())
- error (_("No tracepoints defined, not starting trace"));
- for (breakpoint *b : tracepoint_range)
- {
- if (b->enable_state == bp_enabled)
- any_enabled = 1;
- if ((b->type == bp_fast_tracepoint
- ? may_insert_fast_tracepoints
- : may_insert_tracepoints))
- ++num_to_download;
- else
- warning (_("May not insert %stracepoints, skipping tracepoint %d"),
- (b->type == bp_fast_tracepoint ? "fast " : ""), b->number);
- }
- if (!any_enabled)
- {
- if (target_supports_enable_disable_tracepoint ())
- warning (_("No tracepoints enabled"));
- else
- {
- /* No point in tracing with only disabled tracepoints that
- cannot be re-enabled. */
- error (_("No tracepoints enabled, not starting trace"));
- }
- }
- if (num_to_download <= 0)
- error (_("No tracepoints that may be downloaded, not starting trace"));
- target_trace_init ();
- for (breakpoint *b : tracepoint_range)
- {
- struct tracepoint *t = (struct tracepoint *) b;
- int bp_location_downloaded = 0;
- /* Clear `inserted' flag. */
- for (bp_location *loc : b->locations ())
- loc->inserted = 0;
- if ((b->type == bp_fast_tracepoint
- ? !may_insert_fast_tracepoints
- : !may_insert_tracepoints))
- continue;
- t->number_on_target = 0;
- for (bp_location *loc : b->locations ())
- {
- /* Since tracepoint locations are never duplicated, `inserted'
- flag should be zero. */
- gdb_assert (!loc->inserted);
- target_download_tracepoint (loc);
- loc->inserted = 1;
- bp_location_downloaded = 1;
- }
- t->number_on_target = b->number;
- for (bp_location *loc : b->locations ())
- if (loc->probe.prob != NULL)
- loc->probe.prob->set_semaphore (loc->probe.objfile,
- loc->gdbarch);
- if (bp_location_downloaded)
- gdb::observers::breakpoint_modified.notify (b);
- }
- /* Send down all the trace state variables too. */
- for (const trace_state_variable &tsv : tvariables)
- target_download_trace_state_variable (tsv);
-
- /* Tell target to treat text-like sections as transparent. */
- target_trace_set_readonly_regions ();
- /* Set some mode flags. */
- target_set_disconnected_tracing (disconnected_tracing);
- target_set_circular_trace_buffer (circular_trace_buffer);
- target_set_trace_buffer_size (trace_buffer_size);
- if (!notes)
- notes = trace_notes.c_str ();
- ret = target_set_trace_notes (trace_user.c_str (), notes, NULL);
- if (!ret && (!trace_user.empty () || notes))
- warning (_("Target does not support trace user/notes, info ignored"));
- /* Now insert traps and begin collecting data. */
- target_trace_start ();
- /* Reset our local state. */
- trace_reset_local_state ();
- current_trace_status()->running = 1;
- }
- /* The tstart command requests the target to start a new trace run.
- The command passes any arguments it has to the target verbatim, as
- an optional "trace note". This is useful as for instance a warning
- to other users if the trace runs disconnected, and you don't want
- anybody else messing with the target. */
- static void
- tstart_command (const char *args, int from_tty)
- {
- dont_repeat (); /* Like "run", dangerous to repeat accidentally. */
- if (current_trace_status ()->running)
- {
- if (from_tty
- && !query (_("A trace is running already. Start a new run? ")))
- error (_("New trace run not started."));
- }
- start_tracing (args);
- }
- /* The tstop command stops the tracing run. The command passes any
- supplied arguments to the target verbatim as a "stop note"; if the
- target supports trace notes, then it will be reported back as part
- of the trace run's status. */
- static void
- tstop_command (const char *args, int from_tty)
- {
- if (!current_trace_status ()->running)
- error (_("Trace is not running."));
- stop_tracing (args);
- }
- void
- stop_tracing (const char *note)
- {
- int ret;
- target_trace_stop ();
- for (breakpoint *t : all_tracepoints ())
- {
- if ((t->type == bp_fast_tracepoint
- ? !may_insert_fast_tracepoints
- : !may_insert_tracepoints))
- continue;
- for (bp_location *loc : t->locations ())
- {
- /* GDB can be totally absent in some disconnected trace scenarios,
- but we don't really care if this semaphore goes out of sync.
- That's why we are decrementing it here, but not taking care
- in other places. */
- if (loc->probe.prob != NULL)
- loc->probe.prob->clear_semaphore (loc->probe.objfile,
- loc->gdbarch);
- }
- }
- if (!note)
- note = trace_stop_notes.c_str ();
- ret = target_set_trace_notes (NULL, NULL, note);
- if (!ret && note)
- warning (_("Target does not support trace notes, note ignored"));
- /* Should change in response to reply? */
- current_trace_status ()->running = 0;
- }
- /* tstatus command */
- static void
- tstatus_command (const char *args, int from_tty)
- {
- struct trace_status *ts = current_trace_status ();
- int status;
-
- status = target_get_trace_status (ts);
- if (status == -1)
- {
- if (ts->filename != NULL)
- gdb_printf (_("Using a trace file.\n"));
- else
- {
- gdb_printf (_("Trace can not be run on this target.\n"));
- return;
- }
- }
- if (!ts->running_known)
- {
- gdb_printf (_("Run/stop status is unknown.\n"));
- }
- else if (ts->running)
- {
- gdb_printf (_("Trace is running on the target.\n"));
- }
- else
- {
- switch (ts->stop_reason)
- {
- case trace_never_run:
- gdb_printf (_("No trace has been run on the target.\n"));
- break;
- case trace_stop_command:
- if (ts->stop_desc)
- gdb_printf (_("Trace stopped by a tstop command (%s).\n"),
- ts->stop_desc);
- else
- gdb_printf (_("Trace stopped by a tstop command.\n"));
- break;
- case trace_buffer_full:
- gdb_printf (_("Trace stopped because the buffer was full.\n"));
- break;
- case trace_disconnected:
- gdb_printf (_("Trace stopped because of disconnection.\n"));
- break;
- case tracepoint_passcount:
- gdb_printf (_("Trace stopped by tracepoint %d.\n"),
- ts->stopping_tracepoint);
- break;
- case tracepoint_error:
- if (ts->stopping_tracepoint)
- gdb_printf (_("Trace stopped by an "
- "error (%s, tracepoint %d).\n"),
- ts->stop_desc, ts->stopping_tracepoint);
- else
- gdb_printf (_("Trace stopped by an error (%s).\n"),
- ts->stop_desc);
- break;
- case trace_stop_reason_unknown:
- gdb_printf (_("Trace stopped for an unknown reason.\n"));
- break;
- default:
- gdb_printf (_("Trace stopped for some other reason (%d).\n"),
- ts->stop_reason);
- break;
- }
- }
- if (ts->traceframes_created >= 0
- && ts->traceframe_count != ts->traceframes_created)
- {
- gdb_printf (_("Buffer contains %d trace "
- "frames (of %d created total).\n"),
- ts->traceframe_count, ts->traceframes_created);
- }
- else if (ts->traceframe_count >= 0)
- {
- gdb_printf (_("Collected %d trace frames.\n"),
- ts->traceframe_count);
- }
- if (ts->buffer_free >= 0)
- {
- if (ts->buffer_size >= 0)
- {
- gdb_printf (_("Trace buffer has %d bytes of %d bytes free"),
- ts->buffer_free, ts->buffer_size);
- if (ts->buffer_size > 0)
- gdb_printf (_(" (%d%% full)"),
- ((int) ((((long long) (ts->buffer_size
- - ts->buffer_free)) * 100)
- / ts->buffer_size)));
- gdb_printf (_(".\n"));
- }
- else
- gdb_printf (_("Trace buffer has %d bytes free.\n"),
- ts->buffer_free);
- }
- if (ts->disconnected_tracing)
- gdb_printf (_("Trace will continue if GDB disconnects.\n"));
- else
- gdb_printf (_("Trace will stop if GDB disconnects.\n"));
- if (ts->circular_buffer)
- gdb_printf (_("Trace buffer is circular.\n"));
- if (ts->user_name && strlen (ts->user_name) > 0)
- gdb_printf (_("Trace user is %s.\n"), ts->user_name);
- if (ts->notes && strlen (ts->notes) > 0)
- gdb_printf (_("Trace notes: %s.\n"), ts->notes);
- /* Now report on what we're doing with tfind. */
- if (traceframe_number >= 0)
- gdb_printf (_("Looking at trace frame %d, tracepoint %d.\n"),
- traceframe_number, tracepoint_number);
- else
- gdb_printf (_("Not looking at any trace frame.\n"));
- /* Report start/stop times if supplied. */
- if (ts->start_time)
- {
- if (ts->stop_time)
- {
- LONGEST run_time = ts->stop_time - ts->start_time;
- /* Reporting a run time is more readable than two long numbers. */
- gdb_printf (_("Trace started at %ld.%06ld secs, stopped %ld.%06ld secs later.\n"),
- (long int) (ts->start_time / 1000000),
- (long int) (ts->start_time % 1000000),
- (long int) (run_time / 1000000),
- (long int) (run_time % 1000000));
- }
- else
- gdb_printf (_("Trace started at %ld.%06ld secs.\n"),
- (long int) (ts->start_time / 1000000),
- (long int) (ts->start_time % 1000000));
- }
- else if (ts->stop_time)
- gdb_printf (_("Trace stopped at %ld.%06ld secs.\n"),
- (long int) (ts->stop_time / 1000000),
- (long int) (ts->stop_time % 1000000));
- /* Now report any per-tracepoint status available. */
- for (breakpoint *t : all_tracepoints ())
- target_get_tracepoint_status (t, NULL);
- }
- /* Report the trace status to uiout, in a way suitable for MI, and not
- suitable for CLI. If ON_STOP is true, suppress a few fields that
- are not meaningful in the -trace-stop response.
- The implementation is essentially parallel to trace_status_command, but
- merging them will result in unreadable code. */
- void
- trace_status_mi (int on_stop)
- {
- struct ui_out *uiout = current_uiout;
- struct trace_status *ts = current_trace_status ();
- int status;
- status = target_get_trace_status (ts);
- if (status == -1 && ts->filename == NULL)
- {
- uiout->field_string ("supported", "0");
- return;
- }
- if (ts->filename != NULL)
- uiout->field_string ("supported", "file");
- else if (!on_stop)
- uiout->field_string ("supported", "1");
- if (ts->filename != NULL)
- uiout->field_string ("trace-file", ts->filename);
- gdb_assert (ts->running_known);
- if (ts->running)
- {
- uiout->field_string ("running", "1");
- /* Unlike CLI, do not show the state of 'disconnected-tracing' variable.
- Given that the frontend gets the status either on -trace-stop, or from
- -trace-status after re-connection, it does not seem like this
- information is necessary for anything. It is not necessary for either
- figuring the vital state of the target nor for navigation of trace
- frames. If the frontend wants to show the current state is some
- configure dialog, it can request the value when such dialog is
- invoked by the user. */
- }
- else
- {
- const char *stop_reason = NULL;
- int stopping_tracepoint = -1;
- if (!on_stop)
- uiout->field_string ("running", "0");
- if (ts->stop_reason != trace_stop_reason_unknown)
- {
- switch (ts->stop_reason)
- {
- case trace_stop_command:
- stop_reason = "request";
- break;
- case trace_buffer_full:
- stop_reason = "overflow";
- break;
- case trace_disconnected:
- stop_reason = "disconnection";
- break;
- case tracepoint_passcount:
- stop_reason = "passcount";
- stopping_tracepoint = ts->stopping_tracepoint;
- break;
- case tracepoint_error:
- stop_reason = "error";
- stopping_tracepoint = ts->stopping_tracepoint;
- break;
- }
-
- if (stop_reason)
- {
- uiout->field_string ("stop-reason", stop_reason);
- if (stopping_tracepoint != -1)
- uiout->field_signed ("stopping-tracepoint",
- stopping_tracepoint);
- if (ts->stop_reason == tracepoint_error)
- uiout->field_string ("error-description",
- ts->stop_desc);
- }
- }
- }
- if (ts->traceframe_count != -1)
- uiout->field_signed ("frames", ts->traceframe_count);
- if (ts->traceframes_created != -1)
- uiout->field_signed ("frames-created", ts->traceframes_created);
- if (ts->buffer_size != -1)
- uiout->field_signed ("buffer-size", ts->buffer_size);
- if (ts->buffer_free != -1)
- uiout->field_signed ("buffer-free", ts->buffer_free);
- uiout->field_signed ("disconnected", ts->disconnected_tracing);
- uiout->field_signed ("circular", ts->circular_buffer);
- uiout->field_string ("user-name", ts->user_name);
- uiout->field_string ("notes", ts->notes);
- {
- char buf[100];
- xsnprintf (buf, sizeof buf, "%ld.%06ld",
- (long int) (ts->start_time / 1000000),
- (long int) (ts->start_time % 1000000));
- uiout->field_string ("start-time", buf);
- xsnprintf (buf, sizeof buf, "%ld.%06ld",
- (long int) (ts->stop_time / 1000000),
- (long int) (ts->stop_time % 1000000));
- uiout->field_string ("stop-time", buf);
- }
- }
- /* Check if a trace run is ongoing. If so, and FROM_TTY, query the
- user if she really wants to detach. */
- void
- query_if_trace_running (int from_tty)
- {
- if (!from_tty)
- return;
- /* It can happen that the target that was tracing went away on its
- own, and we didn't notice. Get a status update, and if the
- current target doesn't even do tracing, then assume it's not
- running anymore. */
- if (target_get_trace_status (current_trace_status ()) < 0)
- current_trace_status ()->running = 0;
- /* If running interactively, give the user the option to cancel and
- then decide what to do differently with the run. Scripts are
- just going to disconnect and let the target deal with it,
- according to how it's been instructed previously via
- disconnected-tracing. */
- if (current_trace_status ()->running)
- {
- process_tracepoint_on_disconnect ();
- if (current_trace_status ()->disconnected_tracing)
- {
- if (!query (_("Trace is running and will "
- "continue after detach; detach anyway? ")))
- error (_("Not confirmed."));
- }
- else
- {
- if (!query (_("Trace is running but will "
- "stop on detach; detach anyway? ")))
- error (_("Not confirmed."));
- }
- }
- }
- /* This function handles the details of what to do about an ongoing
- tracing run if the user has asked to detach or otherwise disconnect
- from the target. */
- void
- disconnect_tracing (void)
- {
- /* Also we want to be out of tfind mode, otherwise things can get
- confusing upon reconnection. Just use these calls instead of
- full tfind_1 behavior because we're in the middle of detaching,
- and there's no point to updating current stack frame etc. */
- trace_reset_local_state ();
- }
- /* Worker function for the various flavors of the tfind command. */
- void
- tfind_1 (enum trace_find_type type, int num,
- CORE_ADDR addr1, CORE_ADDR addr2,
- int from_tty)
- {
- int target_frameno = -1, target_tracept = -1;
- struct frame_id old_frame_id = null_frame_id;
- struct tracepoint *tp;
- struct ui_out *uiout = current_uiout;
- /* Only try to get the current stack frame if we have a chance of
- succeeding. In particular, if we're trying to get a first trace
- frame while all threads are running, it's not going to succeed,
- so leave it with a default value and let the frame comparison
- below (correctly) decide to print out the source location of the
- trace frame. */
- if (!(type == tfind_number && num == -1)
- && (has_stack_frames () || traceframe_number >= 0))
- old_frame_id = get_frame_id (get_current_frame ());
- target_frameno = target_trace_find (type, num, addr1, addr2,
- &target_tracept);
-
- if (type == tfind_number
- && num == -1
- && target_frameno == -1)
- {
- /* We told the target to get out of tfind mode, and it did. */
- }
- else if (target_frameno == -1)
- {
- /* A request for a non-existent trace frame has failed.
- Our response will be different, depending on FROM_TTY:
- If FROM_TTY is true, meaning that this command was
- typed interactively by the user, then give an error
- and DO NOT change the state of traceframe_number etc.
- However if FROM_TTY is false, meaning that we're either
- in a script, a loop, or a user-defined command, then
- DON'T give an error, but DO change the state of
- traceframe_number etc. to invalid.
- The rationale is that if you typed the command, you
- might just have committed a typo or something, and you'd
- like to NOT lose your current debugging state. However
- if you're in a user-defined command or especially in a
- loop, then you need a way to detect that the command
- failed WITHOUT aborting. This allows you to write
- scripts that search thru the trace buffer until the end,
- and then continue on to do something else. */
-
- if (from_tty)
- error (_("Target failed to find requested trace frame."));
- else
- {
- if (info_verbose)
- gdb_printf ("End of trace buffer.\n");
- #if 0 /* dubious now? */
- /* The following will not recurse, since it's
- special-cased. */
- tfind_command ("-1", from_tty);
- #endif
- }
- }
-
- tp = get_tracepoint_by_number_on_target (target_tracept);
- reinit_frame_cache ();
- target_dcache_invalidate ();
- set_tracepoint_num (tp ? tp->number : target_tracept);
- if (target_frameno != get_traceframe_number ())
- gdb::observers::traceframe_changed.notify (target_frameno, tracepoint_number);
- set_current_traceframe (target_frameno);
- if (target_frameno == -1)
- set_traceframe_context (NULL);
- else
- set_traceframe_context (get_current_frame ());
- if (traceframe_number >= 0)
- {
- /* Use different branches for MI and CLI to make CLI messages
- i18n-eable. */
- if (uiout->is_mi_like_p ())
- {
- uiout->field_string ("found", "1");
- uiout->field_signed ("tracepoint", tracepoint_number);
- uiout->field_signed ("traceframe", traceframe_number);
- }
- else
- {
- gdb_printf (_("Found trace frame %d, tracepoint %d\n"),
- traceframe_number, tracepoint_number);
- }
- }
- else
- {
- if (uiout->is_mi_like_p ())
- uiout->field_string ("found", "0");
- else if (type == tfind_number && num == -1)
- gdb_printf (_("No longer looking at any trace frame\n"));
- else /* This case may never occur, check. */
- gdb_printf (_("No trace frame found\n"));
- }
- /* If we're in nonstop mode and getting out of looking at trace
- frames, there won't be any current frame to go back to and
- display. */
- if (from_tty
- && (has_stack_frames () || traceframe_number >= 0))
- {
- enum print_what print_what;
- /* NOTE: in imitation of the step command, try to determine
- whether we have made a transition from one function to
- another. If so, we'll print the "stack frame" (ie. the new
- function and it's arguments) -- otherwise we'll just show the
- new source line. */
- if (frame_id_eq (old_frame_id,
- get_frame_id (get_current_frame ())))
- print_what = SRC_LINE;
- else
- print_what = SRC_AND_LOC;
- print_stack_frame (get_selected_frame (NULL), 1, print_what, 1);
- do_displays ();
- }
- }
- /* Error on looking at traceframes while trace is running. */
- void
- check_trace_running (struct trace_status *status)
- {
- if (status->running && status->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
- }
- /* trace_find_command takes a trace frame number n,
- sends "QTFrame:<n>" to the target,
- and accepts a reply that may contain several optional pieces
- of information: a frame number, a tracepoint number, and an
- indication of whether this is a trap frame or a stepping frame.
- The minimal response is just "OK" (which indicates that the
- target does not give us a frame number or a tracepoint number).
- Instead of that, the target may send us a string containing
- any combination of:
- F<hexnum> (gives the selected frame number)
- T<hexnum> (gives the selected tracepoint number)
- */
- /* tfind command */
- static void
- tfind_command_1 (const char *args, int from_tty)
- { /* This should only be called with a numeric argument. */
- int frameno = -1;
- check_trace_running (current_trace_status ());
-
- if (args == 0 || *args == 0)
- { /* TFIND with no args means find NEXT trace frame. */
- if (traceframe_number == -1)
- frameno = 0; /* "next" is first one. */
- else
- frameno = traceframe_number + 1;
- }
- else if (0 == strcmp (args, "-"))
- {
- if (traceframe_number == -1)
- error (_("not debugging trace buffer"));
- else if (from_tty && traceframe_number == 0)
- error (_("already at start of trace buffer"));
-
- frameno = traceframe_number - 1;
- }
- /* A hack to work around eval's need for fp to have been collected. */
- else if (0 == strcmp (args, "-1"))
- frameno = -1;
- else
- frameno = parse_and_eval_long (args);
- if (frameno < -1)
- error (_("invalid input (%d is less than zero)"), frameno);
- tfind_1 (tfind_number, frameno, 0, 0, from_tty);
- }
- static void
- tfind_command (const char *args, int from_tty)
- {
- tfind_command_1 (args, from_tty);
- }
- /* tfind end */
- static void
- tfind_end_command (const char *args, int from_tty)
- {
- tfind_command_1 ("-1", from_tty);
- }
- /* tfind start */
- static void
- tfind_start_command (const char *args, int from_tty)
- {
- tfind_command_1 ("0", from_tty);
- }
- /* tfind pc command */
- static void
- tfind_pc_command (const char *args, int from_tty)
- {
- CORE_ADDR pc;
- check_trace_running (current_trace_status ());
- if (args == 0 || *args == 0)
- pc = regcache_read_pc (get_current_regcache ());
- else
- pc = parse_and_eval_address (args);
- tfind_1 (tfind_pc, 0, pc, 0, from_tty);
- }
- /* tfind tracepoint command */
- static void
- tfind_tracepoint_command (const char *args, int from_tty)
- {
- int tdp;
- struct tracepoint *tp;
- check_trace_running (current_trace_status ());
- if (args == 0 || *args == 0)
- {
- if (tracepoint_number == -1)
- error (_("No current tracepoint -- please supply an argument."));
- else
- tdp = tracepoint_number; /* Default is current TDP. */
- }
- else
- tdp = parse_and_eval_long (args);
- /* If we have the tracepoint on hand, use the number that the
- target knows about (which may be different if we disconnected
- and reconnected). */
- tp = get_tracepoint (tdp);
- if (tp)
- tdp = tp->number_on_target;
- tfind_1 (tfind_tp, tdp, 0, 0, from_tty);
- }
- /* TFIND LINE command:
- This command will take a sourceline for argument, just like BREAK
- or TRACE (ie. anything that "decode_line_1" can handle).
- With no argument, this command will find the next trace frame
- corresponding to a source line OTHER THAN THE CURRENT ONE. */
- static void
- tfind_line_command (const char *args, int from_tty)
- {
- check_trace_running (current_trace_status ());
- symtab_and_line sal;
- if (args == 0 || *args == 0)
- {
- sal = find_pc_line (get_frame_pc (get_current_frame ()), 0);
- }
- else
- {
- std::vector<symtab_and_line> sals
- = decode_line_with_current_source (args, DECODE_LINE_FUNFIRSTLINE);
- sal = sals[0];
- }
- if (sal.symtab == 0)
- error (_("No line number information available."));
- CORE_ADDR start_pc, end_pc;
- if (sal.line > 0 && find_line_pc_range (sal, &start_pc, &end_pc))
- {
- if (start_pc == end_pc)
- {
- gdb_printf ("Line %d of \"%s\"",
- sal.line,
- symtab_to_filename_for_display (sal.symtab));
- gdb_stdout->wrap_here (2);
- gdb_printf (" is at address ");
- print_address (get_current_arch (), start_pc, gdb_stdout);
- gdb_stdout->wrap_here (2);
- gdb_printf (" but contains no code.\n");
- sal = find_pc_line (start_pc, 0);
- if (sal.line > 0
- && find_line_pc_range (sal, &start_pc, &end_pc)
- && start_pc != end_pc)
- gdb_printf ("Attempting to find line %d instead.\n",
- sal.line);
- else
- error (_("Cannot find a good line."));
- }
- }
- else
- {
- /* Is there any case in which we get here, and have an address
- which the user would want to see? If we have debugging
- symbols and no line numbers? */
- error (_("Line number %d is out of range for \"%s\"."),
- sal.line, symtab_to_filename_for_display (sal.symtab));
- }
- /* Find within range of stated line. */
- if (args && *args)
- tfind_1 (tfind_range, 0, start_pc, end_pc - 1, from_tty);
- else
- tfind_1 (tfind_outside, 0, start_pc, end_pc - 1, from_tty);
- }
- /* tfind range command */
- static void
- tfind_range_command (const char *args, int from_tty)
- {
- static CORE_ADDR start, stop;
- const char *tmp;
- check_trace_running (current_trace_status ());
- if (args == 0 || *args == 0)
- { /* XXX FIXME: what should default behavior be? */
- gdb_printf ("Usage: tfind range STARTADDR, ENDADDR\n");
- return;
- }
- if (0 != (tmp = strchr (args, ',')))
- {
- std::string start_addr (args, tmp);
- ++tmp;
- tmp = skip_spaces (tmp);
- start = parse_and_eval_address (start_addr.c_str ());
- stop = parse_and_eval_address (tmp);
- }
- else
- { /* No explicit end address? */
- start = parse_and_eval_address (args);
- stop = start + 1; /* ??? */
- }
- tfind_1 (tfind_range, 0, start, stop, from_tty);
- }
- /* tfind outside command */
- static void
- tfind_outside_command (const char *args, int from_tty)
- {
- CORE_ADDR start, stop;
- const char *tmp;
- if (current_trace_status ()->running
- && current_trace_status ()->filename == NULL)
- error (_("May not look at trace frames while trace is running."));
- if (args == 0 || *args == 0)
- { /* XXX FIXME: what should default behavior be? */
- gdb_printf ("Usage: tfind outside STARTADDR, ENDADDR\n");
- return;
- }
- if (0 != (tmp = strchr (args, ',')))
- {
- std::string start_addr (args, tmp);
- ++tmp;
- tmp = skip_spaces (tmp);
- start = parse_and_eval_address (start_addr.c_str ());
- stop = parse_and_eval_address (tmp);
- }
- else
- { /* No explicit end address? */
- start = parse_and_eval_address (args);
- stop = start + 1; /* ??? */
- }
- tfind_1 (tfind_outside, 0, start, stop, from_tty);
- }
- /* info scope command: list the locals for a scope. */
- static void
- info_scope_command (const char *args_in, int from_tty)
- {
- struct symbol *sym;
- struct bound_minimal_symbol msym;
- const struct block *block;
- const char *symname;
- const char *save_args = args_in;
- struct block_iterator iter;
- int j, count = 0;
- struct gdbarch *gdbarch;
- int regno;
- const char *args = args_in;
- if (args == 0 || *args == 0)
- error (_("requires an argument (function, "
- "line or *addr) to define a scope"));
- event_location_up location = string_to_event_location (&args,
- current_language);
- std::vector<symtab_and_line> sals
- = decode_line_1 (location.get (), DECODE_LINE_FUNFIRSTLINE,
- NULL, NULL, 0);
- if (sals.empty ())
- {
- /* Presumably decode_line_1 has already warned. */
- return;
- }
- /* Resolve line numbers to PC. */
- resolve_sal_pc (&sals[0]);
- block = block_for_pc (sals[0].pc);
- while (block != 0)
- {
- QUIT; /* Allow user to bail out with ^C. */
- ALL_BLOCK_SYMBOLS (block, iter, sym)
- {
- QUIT; /* Allow user to bail out with ^C. */
- if (count == 0)
- gdb_printf ("Scope for %s:\n", save_args);
- count++;
- symname = sym->print_name ();
- if (symname == NULL || *symname == '\0')
- continue; /* Probably botched, certainly useless. */
- gdbarch = symbol_arch (sym);
- gdb_printf ("Symbol %s is ", symname);
- if (SYMBOL_COMPUTED_OPS (sym) != NULL)
- SYMBOL_COMPUTED_OPS (sym)->describe_location (sym,
- BLOCK_ENTRY_PC (block),
- gdb_stdout);
- else
- {
- switch (sym->aclass ())
- {
- default:
- case LOC_UNDEF: /* Messed up symbol? */
- gdb_printf ("a bogus symbol, class %d.\n",
- sym->aclass ());
- count--; /* Don't count this one. */
- continue;
- case LOC_CONST:
- gdb_printf ("a constant with value %s (%s)",
- plongest (SYMBOL_VALUE (sym)),
- hex_string (SYMBOL_VALUE (sym)));
- break;
- case LOC_CONST_BYTES:
- gdb_printf ("constant bytes: ");
- if (sym->type ())
- for (j = 0; j < TYPE_LENGTH (sym->type ()); j++)
- gdb_printf (" %02x",
- (unsigned) SYMBOL_VALUE_BYTES (sym)[j]);
- break;
- case LOC_STATIC:
- gdb_printf ("in static storage at address ");
- gdb_printf ("%s", paddress (gdbarch,
- SYMBOL_VALUE_ADDRESS (sym)));
- break;
- case LOC_REGISTER:
- /* GDBARCH is the architecture associated with the objfile
- the symbol is defined in; the target architecture may be
- different, and may provide additional registers. However,
- we do not know the target architecture at this point.
- We assume the objfile architecture will contain all the
- standard registers that occur in debug info in that
- objfile. */
- regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym,
- gdbarch);
- if (sym->is_argument ())
- gdb_printf ("an argument in register $%s",
- gdbarch_register_name (gdbarch, regno));
- else
- gdb_printf ("a local variable in register $%s",
- gdbarch_register_name (gdbarch, regno));
- break;
- case LOC_ARG:
- gdb_printf ("an argument at stack/frame offset %s",
- plongest (SYMBOL_VALUE (sym)));
- break;
- case LOC_LOCAL:
- gdb_printf ("a local variable at frame offset %s",
- plongest (SYMBOL_VALUE (sym)));
- break;
- case LOC_REF_ARG:
- gdb_printf ("a reference argument at offset %s",
- plongest (SYMBOL_VALUE (sym)));
- break;
- case LOC_REGPARM_ADDR:
- /* Note comment at LOC_REGISTER. */
- regno = SYMBOL_REGISTER_OPS (sym)->register_number (sym,
- gdbarch);
- gdb_printf ("the address of an argument, in register $%s",
- gdbarch_register_name (gdbarch, regno));
- break;
- case LOC_TYPEDEF:
- gdb_printf ("a typedef.\n");
- continue;
- case LOC_LABEL:
- gdb_printf ("a label at address ");
- gdb_printf ("%s", paddress (gdbarch,
- SYMBOL_VALUE_ADDRESS (sym)));
- break;
- case LOC_BLOCK:
- gdb_printf ("a function at address ");
- gdb_printf ("%s",
- paddress (gdbarch, BLOCK_ENTRY_PC (SYMBOL_BLOCK_VALUE (sym))));
- break;
- case LOC_UNRESOLVED:
- msym = lookup_minimal_symbol (sym->linkage_name (),
- NULL, NULL);
- if (msym.minsym == NULL)
- gdb_printf ("Unresolved Static");
- else
- {
- gdb_printf ("static storage at address ");
- gdb_printf ("%s",
- paddress (gdbarch,
- BMSYMBOL_VALUE_ADDRESS (msym)));
- }
- break;
- case LOC_OPTIMIZED_OUT:
- gdb_printf ("optimized out.\n");
- continue;
- case LOC_COMPUTED:
- gdb_assert_not_reached ("LOC_COMPUTED variable missing a method");
- }
- }
- if (sym->type ())
- {
- struct type *t = check_typedef (sym->type ());
- gdb_printf (", length %s.\n", pulongest (TYPE_LENGTH (t)));
- }
- }
- if (BLOCK_FUNCTION (block))
- break;
- else
- block = BLOCK_SUPERBLOCK (block);
- }
- if (count <= 0)
- gdb_printf ("Scope for %s contains no locals or arguments.\n",
- save_args);
- }
- /* Helper for trace_dump_command. Dump the action list starting at
- ACTION. STEPPING_ACTIONS is true if we're iterating over the
- actions of the body of a while-stepping action. STEPPING_FRAME is
- set if the current traceframe was determined to be a while-stepping
- traceframe. */
- static void
- trace_dump_actions (struct command_line *action,
- int stepping_actions, int stepping_frame,
- int from_tty)
- {
- const char *action_exp, *next_comma;
- for (; action != NULL; action = action->next)
- {
- struct cmd_list_element *cmd;
- QUIT; /* Allow user to bail out with ^C. */
- action_exp = action->line;
- action_exp = skip_spaces (action_exp);
- /* The collection actions to be done while stepping are
- bracketed by the commands "while-stepping" and "end". */
- if (*action_exp == '#') /* comment line */
- continue;
- cmd = lookup_cmd (&action_exp, cmdlist, "", NULL, -1, 1);
- if (cmd == 0)
- error (_("Bad action list item: %s"), action_exp);
- if (cmd_simple_func_eq (cmd, while_stepping_pseudocommand))
- {
- gdb_assert (action->body_list_1 == nullptr);
- trace_dump_actions (action->body_list_0.get (),
- 1, stepping_frame, from_tty);
- }
- else if (cmd_simple_func_eq (cmd, collect_pseudocommand))
- {
- /* Display the collected data.
- For the trap frame, display only what was collected at
- the trap. Likewise for stepping frames, display only
- what was collected while stepping. This means that the
- two boolean variables, STEPPING_FRAME and
- STEPPING_ACTIONS should be equal. */
- if (stepping_frame == stepping_actions)
- {
- int trace_string = 0;
- if (*action_exp == '/')
- action_exp = decode_agent_options (action_exp, &trace_string);
- do
- { /* Repeat over a comma-separated list. */
- QUIT; /* Allow user to bail out with ^C. */
- if (*action_exp == ',')
- action_exp++;
- action_exp = skip_spaces (action_exp);
- next_comma = strchr (action_exp, ',');
- if (0 == strncasecmp (action_exp, "$reg", 4))
- registers_info (NULL, from_tty);
- else if (0 == strncasecmp (action_exp, "$_ret", 5))
- ;
- else if (0 == strncasecmp (action_exp, "$loc", 4))
- info_locals_command (NULL, from_tty);
- else if (0 == strncasecmp (action_exp, "$arg", 4))
- info_args_command (NULL, from_tty);
- else
- { /* variable */
- std::string contents;
- const char *exp = action_exp;
- if (next_comma != NULL)
- {
- size_t len = next_comma - action_exp;
- contents = std::string (action_exp, len);
- exp = contents.c_str ();
- }
- gdb_printf ("%s = ", exp);
- output_command (exp, from_tty);
- gdb_printf ("\n");
- }
- action_exp = next_comma;
- }
- while (action_exp && *action_exp == ',');
- }
- }
- }
- }
- /* Return bp_location of the tracepoint associated with the current
- traceframe. Set *STEPPING_FRAME_P to 1 if the current traceframe
- is a stepping traceframe. */
- struct bp_location *
- get_traceframe_location (int *stepping_frame_p)
- {
- struct tracepoint *t;
- struct regcache *regcache;
- if (tracepoint_number == -1)
- error (_("No current trace frame."));
- t = get_tracepoint (tracepoint_number);
- if (t == NULL)
- error (_("No known tracepoint matches 'current' tracepoint #%d."),
- tracepoint_number);
- /* The current frame is a trap frame if the frame PC is equal to the
- tracepoint PC. If not, then the current frame was collected
- during single-stepping. */
- regcache = get_current_regcache ();
- /* If the traceframe's address matches any of the tracepoint's
- locations, assume it is a direct hit rather than a while-stepping
- frame. (FIXME this is not reliable, should record each frame's
- type.) */
- for (bp_location *tloc : t->locations ())
- if (tloc->address == regcache_read_pc (regcache))
- {
- *stepping_frame_p = 0;
- return tloc;
- }
- /* If this is a stepping frame, we don't know which location
- triggered. The first is as good (or bad) a guess as any... */
- *stepping_frame_p = 1;
- return t->loc;
- }
- /* Return the default collect actions of a tracepoint T. */
- static counted_command_line
- all_tracepoint_actions (struct breakpoint *t)
- {
- counted_command_line actions (nullptr, command_lines_deleter ());
- /* If there are default expressions to collect, make up a collect
- action and prepend to the action list to encode. Note that since
- validation is per-tracepoint (local var "xyz" might be valid for
- one tracepoint and not another, etc), we make up the action on
- the fly, and don't cache it. */
- if (!default_collect.empty ())
- {
- gdb::unique_xmalloc_ptr<char> default_collect_line
- = xstrprintf ("collect %s", default_collect.c_str ());
- validate_actionline (default_collect_line.get (), t);
- actions.reset (new struct command_line (simple_control,
- default_collect_line.release ()),
- command_lines_deleter ());
- }
- return actions;
- }
- /* The tdump command. */
- static void
- tdump_command (const char *args, int from_tty)
- {
- int stepping_frame = 0;
- struct bp_location *loc;
- /* This throws an error is not inspecting a trace frame. */
- loc = get_traceframe_location (&stepping_frame);
- gdb_printf ("Data collected at tracepoint %d, trace frame %d:\n",
- tracepoint_number, traceframe_number);
- /* This command only makes sense for the current frame, not the
- selected frame. */
- scoped_restore_current_thread restore_thread;
- select_frame (get_current_frame ());
- counted_command_line actions = all_tracepoint_actions (loc->owner);
- trace_dump_actions (actions.get (), 0, stepping_frame, from_tty);
- trace_dump_actions (breakpoint_commands (loc->owner), 0, stepping_frame,
- from_tty);
- }
- /* Encode a piece of a tracepoint's source-level definition in a form
- that is suitable for both protocol and saving in files. */
- /* This version does not do multiple encodes for long strings; it should
- return an offset to the next piece to encode. FIXME */
- int
- encode_source_string (int tpnum, ULONGEST addr,
- const char *srctype, const char *src,
- char *buf, int buf_size)
- {
- if (80 + strlen (srctype) > buf_size)
- error (_("Buffer too small for source encoding"));
- sprintf (buf, "%x:%s:%s:%x:%x:",
- tpnum, phex_nz (addr, sizeof (addr)),
- srctype, 0, (int) strlen (src));
- if (strlen (buf) + strlen (src) * 2 >= buf_size)
- error (_("Source string too long for buffer"));
- bin2hex ((gdb_byte *) src, buf + strlen (buf), strlen (src));
- return -1;
- }
- /* Tell the target what to do with an ongoing tracing run if GDB
- disconnects for some reason. */
- static void
- set_disconnected_tracing (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- target_set_disconnected_tracing (disconnected_tracing);
- }
- static void
- set_circular_trace_buffer (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- target_set_circular_trace_buffer (circular_trace_buffer);
- }
- static void
- set_trace_buffer_size (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- target_set_trace_buffer_size (trace_buffer_size);
- }
- static void
- set_trace_user (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- int ret;
- ret = target_set_trace_notes (trace_user.c_str (), NULL, NULL);
- if (!ret)
- warning (_("Target does not support trace notes, user ignored"));
- }
- static void
- set_trace_notes (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- int ret;
- ret = target_set_trace_notes (NULL, trace_notes.c_str (), NULL);
- if (!ret)
- warning (_("Target does not support trace notes, note ignored"));
- }
- static void
- set_trace_stop_notes (const char *args, int from_tty,
- struct cmd_list_element *c)
- {
- int ret;
- ret = target_set_trace_notes (NULL, NULL, trace_stop_notes.c_str ());
- if (!ret)
- warning (_("Target does not support trace notes, stop note ignored"));
- }
- /* Convert the memory pointed to by mem into hex, placing result in buf.
- * Return a pointer to the last char put in buf (null)
- * "stolen" from sparc-stub.c
- */
- static const char hexchars[] = "0123456789abcdef";
- static char *
- mem2hex (gdb_byte *mem, char *buf, int count)
- {
- gdb_byte ch;
- while (count-- > 0)
- {
- ch = *mem++;
- *buf++ = hexchars[ch >> 4];
- *buf++ = hexchars[ch & 0xf];
- }
- *buf = 0;
- return buf;
- }
- int
- get_traceframe_number (void)
- {
- return traceframe_number;
- }
- int
- get_tracepoint_number (void)
- {
- return tracepoint_number;
- }
- /* Make the traceframe NUM be the current trace frame. Does nothing
- if NUM is already current. */
- void
- set_current_traceframe (int num)
- {
- int newnum;
- if (traceframe_number == num)
- {
- /* Nothing to do. */
- return;
- }
- newnum = target_trace_find (tfind_number, num, 0, 0, NULL);
- if (newnum != num)
- warning (_("could not change traceframe"));
- set_traceframe_num (newnum);
- /* Changing the traceframe changes our view of registers and of the
- frame chain. */
- registers_changed ();
- clear_traceframe_info ();
- }
- scoped_restore_current_traceframe::scoped_restore_current_traceframe ()
- : m_traceframe_number (traceframe_number)
- {}
- /* Given a number and address, return an uploaded tracepoint with that
- number, creating if necessary. */
- struct uploaded_tp *
- get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp)
- {
- struct uploaded_tp *utp;
- for (utp = *utpp; utp; utp = utp->next)
- if (utp->number == num && utp->addr == addr)
- return utp;
- utp = new uploaded_tp;
- utp->number = num;
- utp->addr = addr;
- utp->next = *utpp;
- *utpp = utp;
- return utp;
- }
- void
- free_uploaded_tps (struct uploaded_tp **utpp)
- {
- struct uploaded_tp *next_one;
- while (*utpp)
- {
- next_one = (*utpp)->next;
- delete *utpp;
- *utpp = next_one;
- }
- }
- /* Given a number and address, return an uploaded tracepoint with that
- number, creating if necessary. */
- struct uploaded_tsv *
- get_uploaded_tsv (int num, struct uploaded_tsv **utsvp)
- {
- struct uploaded_tsv *utsv;
- for (utsv = *utsvp; utsv; utsv = utsv->next)
- if (utsv->number == num)
- return utsv;
- utsv = XCNEW (struct uploaded_tsv);
- utsv->number = num;
- utsv->next = *utsvp;
- *utsvp = utsv;
- return utsv;
- }
- void
- free_uploaded_tsvs (struct uploaded_tsv **utsvp)
- {
- struct uploaded_tsv *next_one;
- while (*utsvp)
- {
- next_one = (*utsvp)->next;
- xfree (*utsvp);
- *utsvp = next_one;
- }
- }
- /* FIXME this function is heuristic and will miss the cases where the
- conditional is semantically identical but differs in whitespace,
- such as "x == 0" vs "x==0". */
- static int
- cond_string_is_same (char *str1, char *str2)
- {
- if (str1 == NULL || str2 == NULL)
- return (str1 == str2);
- return (strcmp (str1, str2) == 0);
- }
- /* Look for an existing tracepoint that seems similar enough to the
- uploaded one. Enablement isn't compared, because the user can
- toggle that freely, and may have done so in anticipation of the
- next trace run. Return the location of matched tracepoint. */
- static struct bp_location *
- find_matching_tracepoint_location (struct uploaded_tp *utp)
- {
- struct bp_location *loc;
- for (breakpoint *b : all_tracepoints ())
- {
- struct tracepoint *t = (struct tracepoint *) b;
- if (b->type == utp->type
- && t->step_count == utp->step
- && t->pass_count == utp->pass
- && cond_string_is_same (t->cond_string.get (),
- utp->cond_string.get ())
- /* FIXME also test actions. */
- )
- {
- /* Scan the locations for an address match. */
- for (loc = b->loc; loc; loc = loc->next)
- {
- if (loc->address == utp->addr)
- return loc;
- }
- }
- }
- return NULL;
- }
- /* Given a list of tracepoints uploaded from a target, attempt to
- match them up with existing tracepoints, and create new ones if not
- found. */
- void
- merge_uploaded_tracepoints (struct uploaded_tp **uploaded_tps)
- {
- struct uploaded_tp *utp;
- /* A set of tracepoints which are modified. */
- std::vector<breakpoint *> modified_tp;
- /* Look for GDB tracepoints that match up with our uploaded versions. */
- for (utp = *uploaded_tps; utp; utp = utp->next)
- {
- struct bp_location *loc;
- struct tracepoint *t;
- loc = find_matching_tracepoint_location (utp);
- if (loc)
- {
- int found = 0;
- /* Mark this location as already inserted. */
- loc->inserted = 1;
- t = (struct tracepoint *) loc->owner;
- gdb_printf (_("Assuming tracepoint %d is same "
- "as target's tracepoint %d at %s.\n"),
- loc->owner->number, utp->number,
- paddress (loc->gdbarch, utp->addr));
- /* The tracepoint LOC->owner was modified (the location LOC
- was marked as inserted in the target). Save it in
- MODIFIED_TP if not there yet. The 'breakpoint-modified'
- observers will be notified later once for each tracepoint
- saved in MODIFIED_TP. */
- for (breakpoint *b : modified_tp)
- if (b == loc->owner)
- {
- found = 1;
- break;
- }
- if (!found)
- modified_tp.push_back (loc->owner);
- }
- else
- {
- t = create_tracepoint_from_upload (utp);
- if (t)
- gdb_printf (_("Created tracepoint %d for "
- "target's tracepoint %d at %s.\n"),
- t->number, utp->number,
- paddress (get_current_arch (), utp->addr));
- else
- gdb_printf (_("Failed to create tracepoint for target's "
- "tracepoint %d at %s, skipping it.\n"),
- utp->number,
- paddress (get_current_arch (), utp->addr));
- }
- /* Whether found or created, record the number used by the
- target, to help with mapping target tracepoints back to their
- counterparts here. */
- if (t)
- t->number_on_target = utp->number;
- }
- /* Notify 'breakpoint-modified' observer that at least one of B's
- locations was changed. */
- for (breakpoint *b : modified_tp)
- gdb::observers::breakpoint_modified.notify (b);
- free_uploaded_tps (uploaded_tps);
- }
- /* Trace state variables don't have much to identify them beyond their
- name, so just use that to detect matches. */
- static struct trace_state_variable *
- find_matching_tsv (struct uploaded_tsv *utsv)
- {
- if (!utsv->name)
- return NULL;
- return find_trace_state_variable (utsv->name);
- }
- static struct trace_state_variable *
- create_tsv_from_upload (struct uploaded_tsv *utsv)
- {
- const char *namebase;
- std::string buf;
- int try_num = 0;
- struct trace_state_variable *tsv;
- if (utsv->name)
- {
- namebase = utsv->name;
- buf = namebase;
- }
- else
- {
- namebase = "__tsv";
- buf = string_printf ("%s_%d", namebase, try_num++);
- }
- /* Fish for a name that is not in use. */
- /* (should check against all internal vars?) */
- while (find_trace_state_variable (buf.c_str ()))
- buf = string_printf ("%s_%d", namebase, try_num++);
- /* We have an available name, create the variable. */
- tsv = create_trace_state_variable (buf.c_str ());
- tsv->initial_value = utsv->initial_value;
- tsv->builtin = utsv->builtin;
- gdb::observers::tsv_created.notify (tsv);
- return tsv;
- }
- /* Given a list of uploaded trace state variables, try to match them
- up with existing variables, or create additional ones. */
- void
- merge_uploaded_trace_state_variables (struct uploaded_tsv **uploaded_tsvs)
- {
- struct uploaded_tsv *utsv;
- int highest;
- /* Most likely some numbers will have to be reassigned as part of
- the merge, so clear them all in anticipation. */
- for (trace_state_variable &tsv : tvariables)
- tsv.number = 0;
- for (utsv = *uploaded_tsvs; utsv; utsv = utsv->next)
- {
- struct trace_state_variable *tsv = find_matching_tsv (utsv);
- if (tsv)
- {
- if (info_verbose)
- gdb_printf (_("Assuming trace state variable $%s "
- "is same as target's variable %d.\n"),
- tsv->name.c_str (), utsv->number);
- }
- else
- {
- tsv = create_tsv_from_upload (utsv);
- if (info_verbose)
- gdb_printf (_("Created trace state variable "
- "$%s for target's variable %d.\n"),
- tsv->name.c_str (), utsv->number);
- }
- /* Give precedence to numberings that come from the target. */
- if (tsv)
- tsv->number = utsv->number;
- }
- /* Renumber everything that didn't get a target-assigned number. */
- highest = 0;
- for (const trace_state_variable &tsv : tvariables)
- highest = std::max (tsv.number, highest);
- ++highest;
- for (trace_state_variable &tsv : tvariables)
- if (tsv.number == 0)
- tsv.number = highest++;
- free_uploaded_tsvs (uploaded_tsvs);
- }
- /* Parse the part of trace status syntax that is shared between
- the remote protocol and the trace file reader. */
- void
- parse_trace_status (const char *line, struct trace_status *ts)
- {
- const char *p = line, *p1, *p2, *p3, *p_temp;
- int end;
- ULONGEST val;
- ts->running_known = 1;
- ts->running = (*p++ == '1');
- ts->stop_reason = trace_stop_reason_unknown;
- xfree (ts->stop_desc);
- ts->stop_desc = NULL;
- ts->traceframe_count = -1;
- ts->traceframes_created = -1;
- ts->buffer_free = -1;
- ts->buffer_size = -1;
- ts->disconnected_tracing = 0;
- ts->circular_buffer = 0;
- xfree (ts->user_name);
- ts->user_name = NULL;
- xfree (ts->notes);
- ts->notes = NULL;
- ts->start_time = ts->stop_time = 0;
- while (*p++)
- {
- p1 = strchr (p, ':');
- if (p1 == NULL)
- error (_("Malformed trace status, at %s\n\
- Status line: '%s'\n"), p, line);
- p3 = strchr (p, ';');
- if (p3 == NULL)
- p3 = p + strlen (p);
- if (strncmp (p, stop_reason_names[trace_buffer_full], p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->stop_reason = trace_buffer_full;
- }
- else if (strncmp (p, stop_reason_names[trace_never_run], p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->stop_reason = trace_never_run;
- }
- else if (strncmp (p, stop_reason_names[tracepoint_passcount],
- p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->stop_reason = tracepoint_passcount;
- ts->stopping_tracepoint = val;
- }
- else if (strncmp (p, stop_reason_names[trace_stop_command], p1 - p) == 0)
- {
- p2 = strchr (++p1, ':');
- if (!p2 || p2 > p3)
- {
- /*older style*/
- p2 = p1;
- }
- else if (p2 != p1)
- {
- ts->stop_desc = (char *) xmalloc (strlen (line));
- end = hex2bin (p1, (gdb_byte *) ts->stop_desc, (p2 - p1) / 2);
- ts->stop_desc[end] = '\0';
- }
- else
- ts->stop_desc = xstrdup ("");
- p = unpack_varlen_hex (++p2, &val);
- ts->stop_reason = trace_stop_command;
- }
- else if (strncmp (p, stop_reason_names[trace_disconnected], p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->stop_reason = trace_disconnected;
- }
- else if (strncmp (p, stop_reason_names[tracepoint_error], p1 - p) == 0)
- {
- p2 = strchr (++p1, ':');
- if (p2 != p1)
- {
- ts->stop_desc = (char *) xmalloc ((p2 - p1) / 2 + 1);
- end = hex2bin (p1, (gdb_byte *) ts->stop_desc, (p2 - p1) / 2);
- ts->stop_desc[end] = '\0';
- }
- else
- ts->stop_desc = xstrdup ("");
- p = unpack_varlen_hex (++p2, &val);
- ts->stopping_tracepoint = val;
- ts->stop_reason = tracepoint_error;
- }
- else if (strncmp (p, "tframes", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->traceframe_count = val;
- }
- else if (strncmp (p, "tcreated", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->traceframes_created = val;
- }
- else if (strncmp (p, "tfree", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->buffer_free = val;
- }
- else if (strncmp (p, "tsize", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->buffer_size = val;
- }
- else if (strncmp (p, "disconn", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->disconnected_tracing = val;
- }
- else if (strncmp (p, "circular", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->circular_buffer = val;
- }
- else if (strncmp (p, "starttime", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->start_time = val;
- }
- else if (strncmp (p, "stoptime", p1 - p) == 0)
- {
- p = unpack_varlen_hex (++p1, &val);
- ts->stop_time = val;
- }
- else if (strncmp (p, "username", p1 - p) == 0)
- {
- ++p1;
- ts->user_name = (char *) xmalloc (strlen (p) / 2);
- end = hex2bin (p1, (gdb_byte *) ts->user_name, (p3 - p1) / 2);
- ts->user_name[end] = '\0';
- p = p3;
- }
- else if (strncmp (p, "notes", p1 - p) == 0)
- {
- ++p1;
- ts->notes = (char *) xmalloc (strlen (p) / 2);
- end = hex2bin (p1, (gdb_byte *) ts->notes, (p3 - p1) / 2);
- ts->notes[end] = '\0';
- p = p3;
- }
- else
- {
- /* Silently skip unknown optional info. */
- p_temp = strchr (p1 + 1, ';');
- if (p_temp)
- p = p_temp;
- else
- /* Must be at the end. */
- break;
- }
- }
- }
- void
- parse_tracepoint_status (const char *p, struct breakpoint *bp,
- struct uploaded_tp *utp)
- {
- ULONGEST uval;
- struct tracepoint *tp = (struct tracepoint *) bp;
- p = unpack_varlen_hex (p, &uval);
- if (tp)
- tp->hit_count += uval;
- else
- utp->hit_count += uval;
- p = unpack_varlen_hex (p + 1, &uval);
- if (tp)
- tp->traceframe_usage += uval;
- else
- utp->traceframe_usage += uval;
- /* Ignore any extra, allowing for future extensions. */
- }
- /* Given a line of text defining a part of a tracepoint, parse it into
- an "uploaded tracepoint". */
- void
- parse_tracepoint_definition (const char *line, struct uploaded_tp **utpp)
- {
- const char *p;
- char piece;
- ULONGEST num, addr, step, pass, orig_size, xlen, start;
- int enabled, end;
- enum bptype type;
- const char *srctype;
- char *buf;
- struct uploaded_tp *utp = NULL;
- p = line;
- /* Both tracepoint and action definitions start with the same number
- and address sequence. */
- piece = *p++;
- p = unpack_varlen_hex (p, &num);
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &addr);
- p++; /* skip a colon */
- if (piece == 'T')
- {
- gdb::unique_xmalloc_ptr<char[]> cond;
- enabled = (*p++ == 'E');
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &step);
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &pass);
- type = bp_tracepoint;
- /* Thumb through optional fields. */
- while (*p == ':')
- {
- p++; /* skip a colon */
- if (*p == 'F')
- {
- type = bp_fast_tracepoint;
- p++;
- p = unpack_varlen_hex (p, &orig_size);
- }
- else if (*p == 'S')
- {
- type = bp_static_tracepoint;
- p++;
- }
- else if (*p == 'X')
- {
- p++;
- p = unpack_varlen_hex (p, &xlen);
- p++; /* skip a comma */
- cond.reset ((char *) xmalloc (2 * xlen + 1));
- strncpy (&cond[0], p, 2 * xlen);
- cond[2 * xlen] = '\0';
- p += 2 * xlen;
- }
- else
- warning (_("Unrecognized char '%c' in tracepoint "
- "definition, skipping rest"), *p);
- }
- utp = get_uploaded_tp (num, addr, utpp);
- utp->type = type;
- utp->enabled = enabled;
- utp->step = step;
- utp->pass = pass;
- utp->cond = std::move (cond);
- }
- else if (piece == 'A')
- {
- utp = get_uploaded_tp (num, addr, utpp);
- utp->actions.emplace_back (xstrdup (p));
- }
- else if (piece == 'S')
- {
- utp = get_uploaded_tp (num, addr, utpp);
- utp->step_actions.emplace_back (xstrdup (p));
- }
- else if (piece == 'Z')
- {
- /* Parse a chunk of source form definition. */
- utp = get_uploaded_tp (num, addr, utpp);
- srctype = p;
- p = strchr (p, ':');
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &start);
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &xlen);
- p++; /* skip a colon */
- buf = (char *) alloca (strlen (line));
- end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
- buf[end] = '\0';
- if (startswith (srctype, "at:"))
- utp->at_string.reset (xstrdup (buf));
- else if (startswith (srctype, "cond:"))
- utp->cond_string.reset (xstrdup (buf));
- else if (startswith (srctype, "cmd:"))
- utp->cmd_strings.emplace_back (xstrdup (buf));
- }
- else if (piece == 'V')
- {
- utp = get_uploaded_tp (num, addr, utpp);
- parse_tracepoint_status (p, NULL, utp);
- }
- else
- {
- /* Don't error out, the target might be sending us optional
- info that we don't care about. */
- warning (_("Unrecognized tracepoint piece '%c', ignoring"), piece);
- }
- }
- /* Convert a textual description of a trace state variable into an
- uploaded object. */
- void
- parse_tsv_definition (const char *line, struct uploaded_tsv **utsvp)
- {
- const char *p;
- char *buf;
- ULONGEST num, initval, builtin;
- int end;
- struct uploaded_tsv *utsv = NULL;
- buf = (char *) alloca (strlen (line));
- p = line;
- p = unpack_varlen_hex (p, &num);
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &initval);
- p++; /* skip a colon */
- p = unpack_varlen_hex (p, &builtin);
- p++; /* skip a colon */
- end = hex2bin (p, (gdb_byte *) buf, strlen (p) / 2);
- buf[end] = '\0';
- utsv = get_uploaded_tsv (num, utsvp);
- utsv->initial_value = initval;
- utsv->builtin = builtin;
- utsv->name = xstrdup (buf);
- }
- /* Given a line of text defining a static tracepoint marker, parse it
- into a "static tracepoint marker" object. Throws an error is
- parsing fails. If PP is non-null, it points to one past the end of
- the parsed marker definition. */
- void
- parse_static_tracepoint_marker_definition (const char *line, const char **pp,
- static_tracepoint_marker *marker)
- {
- const char *p, *endp;
- ULONGEST addr;
- p = line;
- p = unpack_varlen_hex (p, &addr);
- p++; /* skip a colon */
- marker->gdbarch = target_gdbarch ();
- marker->address = (CORE_ADDR) addr;
- endp = strchr (p, ':');
- if (endp == NULL)
- error (_("bad marker definition: %s"), line);
- marker->str_id = hex2str (p, (endp - p) / 2);
- p = endp;
- p++; /* skip a colon */
- /* This definition may be followed by another one, separated by a comma. */
- int hex_len;
- endp = strchr (p, ',');
- if (endp != nullptr)
- hex_len = endp - p;
- else
- hex_len = strlen (p);
- marker->extra = hex2str (p, hex_len / 2);
- if (pp != nullptr)
- *pp = p + hex_len;
- }
- /* Print MARKER to gdb_stdout. */
- static void
- print_one_static_tracepoint_marker (int count,
- const static_tracepoint_marker &marker)
- {
- struct symbol *sym;
- struct ui_out *uiout = current_uiout;
- symtab_and_line sal;
- sal.pc = marker.address;
- std::vector<breakpoint *> tracepoints
- = static_tracepoints_here (marker.address);
- ui_out_emit_tuple tuple_emitter (uiout, "marker");
- /* A counter field to help readability. This is not a stable
- identifier! */
- uiout->field_signed ("count", count);
- uiout->field_string ("marker-id", marker.str_id);
- uiout->field_fmt ("enabled", "%c",
- !tracepoints.empty () ? 'y' : 'n');
- uiout->spaces (2);
- int wrap_indent = 35;
- if (gdbarch_addr_bit (marker.gdbarch) <= 32)
- wrap_indent += 11;
- else
- wrap_indent += 19;
- const char *extra_field_indent = " ";
- uiout->field_core_addr ("addr", marker.gdbarch, marker.address);
- sal = find_pc_line (marker.address, 0);
- sym = find_pc_sect_function (marker.address, NULL);
- if (sym)
- {
- uiout->text ("in ");
- uiout->field_string ("func", sym->print_name (),
- function_name_style.style ());
- uiout->wrap_hint (wrap_indent);
- uiout->text (" at ");
- }
- else
- uiout->field_skip ("func");
- if (sal.symtab != NULL)
- {
- uiout->field_string ("file",
- symtab_to_filename_for_display (sal.symtab),
- file_name_style.style ());
- uiout->text (":");
- if (uiout->is_mi_like_p ())
- {
- const char *fullname = symtab_to_fullname (sal.symtab);
- uiout->field_string ("fullname", fullname);
- }
- else
- uiout->field_skip ("fullname");
- uiout->field_signed ("line", sal.line);
- }
- else
- {
- uiout->field_skip ("fullname");
- uiout->field_skip ("line");
- }
- uiout->text ("\n");
- uiout->text (extra_field_indent);
- uiout->text (_("Data: \""));
- uiout->field_string ("extra-data", marker.extra);
- uiout->text ("\"\n");
- if (!tracepoints.empty ())
- {
- int ix;
- {
- ui_out_emit_tuple inner_tuple_emitter (uiout, "tracepoints-at");
- uiout->text (extra_field_indent);
- uiout->text (_("Probed by static tracepoints: "));
- for (ix = 0; ix < tracepoints.size (); ix++)
- {
- if (ix > 0)
- uiout->text (", ");
- uiout->text ("#");
- uiout->field_signed ("tracepoint-id", tracepoints[ix]->number);
- }
- }
- if (uiout->is_mi_like_p ())
- uiout->field_signed ("number-of-tracepoints", tracepoints.size ());
- else
- uiout->text ("\n");
- }
- }
- static void
- info_static_tracepoint_markers_command (const char *arg, int from_tty)
- {
- struct ui_out *uiout = current_uiout;
- std::vector<static_tracepoint_marker> markers
- = target_static_tracepoint_markers_by_strid (NULL);
- /* We don't have to check target_can_use_agent and agent's capability on
- static tracepoint here, in order to be compatible with older GDBserver.
- We don't check USE_AGENT is true or not, because static tracepoints
- don't work without in-process agent, so we don't bother users to type
- `set agent on' when to use static tracepoint. */
- ui_out_emit_table table_emitter (uiout, 5, -1,
- "StaticTracepointMarkersTable");
- uiout->table_header (7, ui_left, "counter", "Cnt");
- uiout->table_header (40, ui_left, "marker-id", "ID");
- uiout->table_header (3, ui_left, "enabled", "Enb");
- if (gdbarch_addr_bit (target_gdbarch ()) <= 32)
- uiout->table_header (10, ui_left, "addr", "Address");
- else
- uiout->table_header (18, ui_left, "addr", "Address");
- uiout->table_header (40, ui_noalign, "what", "What");
- uiout->table_body ();
- for (int i = 0; i < markers.size (); i++)
- print_one_static_tracepoint_marker (i + 1, markers[i]);
- }
- /* The $_sdata convenience variable is a bit special. We don't know
- for sure type of the value until we actually have a chance to fetch
- the data --- the size of the object depends on what has been
- collected. We solve this by making $_sdata be an internalvar that
- creates a new value on access. */
- /* Return a new value with the correct type for the sdata object of
- the current trace frame. Return a void value if there's no object
- available. */
- static struct value *
- sdata_make_value (struct gdbarch *gdbarch, struct internalvar *var,
- void *ignore)
- {
- /* We need to read the whole object before we know its size. */
- gdb::optional<gdb::byte_vector> buf
- = target_read_alloc (current_inferior ()->top_target (),
- TARGET_OBJECT_STATIC_TRACE_DATA,
- NULL);
- if (buf)
- {
- struct value *v;
- struct type *type;
- type = init_vector_type (builtin_type (gdbarch)->builtin_true_char,
- buf->size ());
- v = allocate_value (type);
- memcpy (value_contents_raw (v).data (), buf->data (), buf->size ());
- return v;
- }
- else
- return allocate_value (builtin_type (gdbarch)->builtin_void);
- }
- #if !defined(HAVE_LIBEXPAT)
- struct std::unique_ptr<traceframe_info>
- parse_traceframe_info (const char *tframe_info)
- {
- static int have_warned;
- if (!have_warned)
- {
- have_warned = 1;
- warning (_("Can not parse XML trace frame info; XML support "
- "was disabled at compile time"));
- }
- return NULL;
- }
- #else /* HAVE_LIBEXPAT */
- #include "xml-support.h"
- /* Handle the start of a <memory> element. */
- static void
- traceframe_info_start_memory (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct traceframe_info *info = (struct traceframe_info *) user_data;
- ULONGEST *start_p, *length_p;
- start_p
- = (ULONGEST *) xml_find_attribute (attributes, "start")->value.get ();
- length_p
- = (ULONGEST *) xml_find_attribute (attributes, "length")->value.get ();
- info->memory.emplace_back (*start_p, *length_p);
- }
- /* Handle the start of a <tvar> element. */
- static void
- traceframe_info_start_tvar (struct gdb_xml_parser *parser,
- const struct gdb_xml_element *element,
- void *user_data,
- std::vector<gdb_xml_value> &attributes)
- {
- struct traceframe_info *info = (struct traceframe_info *) user_data;
- const char *id_attrib
- = (const char *) xml_find_attribute (attributes, "id")->value.get ();
- int id = gdb_xml_parse_ulongest (parser, id_attrib);
- info->tvars.push_back (id);
- }
- /* The allowed elements and attributes for an XML memory map. */
- static const struct gdb_xml_attribute memory_attributes[] = {
- { "start", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { "length", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_attribute tvar_attributes[] = {
- { "id", GDB_XML_AF_NONE, NULL, NULL },
- { NULL, GDB_XML_AF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element traceframe_info_children[] = {
- { "memory", memory_attributes, NULL,
- GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
- traceframe_info_start_memory, NULL },
- { "tvar", tvar_attributes, NULL,
- GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL,
- traceframe_info_start_tvar, NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- static const struct gdb_xml_element traceframe_info_elements[] = {
- { "traceframe-info", NULL, traceframe_info_children, GDB_XML_EF_NONE,
- NULL, NULL },
- { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
- };
- /* Parse a traceframe-info XML document. */
- traceframe_info_up
- parse_traceframe_info (const char *tframe_info)
- {
- traceframe_info_up result (new traceframe_info);
- if (gdb_xml_parse_quick (_("trace frame info"),
- "traceframe-info.dtd", traceframe_info_elements,
- tframe_info, result.get ()) == 0)
- return result;
- return NULL;
- }
- #endif /* HAVE_LIBEXPAT */
- /* Returns the traceframe_info object for the current traceframe.
- This is where we avoid re-fetching the object from the target if we
- already have it cached. */
- struct traceframe_info *
- get_traceframe_info (void)
- {
- if (current_traceframe_info == NULL)
- current_traceframe_info = target_traceframe_info ();
- return current_traceframe_info.get ();
- }
- /* If the target supports the query, return in RESULT the set of
- collected memory in the current traceframe, found within the LEN
- bytes range starting at MEMADDR. Returns true if the target
- supports the query, otherwise returns false, and RESULT is left
- undefined. */
- int
- traceframe_available_memory (std::vector<mem_range> *result,
- CORE_ADDR memaddr, ULONGEST len)
- {
- struct traceframe_info *info = get_traceframe_info ();
- if (info != NULL)
- {
- result->clear ();
- for (mem_range &r : info->memory)
- if (mem_ranges_overlap (r.start, r.length, memaddr, len))
- {
- ULONGEST lo1, hi1, lo2, hi2;
- lo1 = memaddr;
- hi1 = memaddr + len;
- lo2 = r.start;
- hi2 = r.start + r.length;
- CORE_ADDR start = std::max (lo1, lo2);
- int length = std::min (hi1, hi2) - start;
- result->emplace_back (start, length);
- }
- normalize_mem_ranges (result);
- return 1;
- }
- return 0;
- }
- /* Implementation of `sdata' variable. */
- static const struct internalvar_funcs sdata_funcs =
- {
- sdata_make_value,
- NULL
- };
- /* See tracepoint.h. */
- cmd_list_element *while_stepping_cmd_element = nullptr;
- /* module initialization */
- void _initialize_tracepoint ();
- void
- _initialize_tracepoint ()
- {
- struct cmd_list_element *c;
- /* Explicitly create without lookup, since that tries to create a
- value with a void typed value, and when we get here, gdbarch
- isn't initialized yet. At this point, we're quite sure there
- isn't another convenience variable of the same name. */
- create_internalvar_type_lazy ("_sdata", &sdata_funcs, NULL);
- traceframe_number = -1;
- tracepoint_number = -1;
- add_info ("scope", info_scope_command,
- _("List the variables local to a scope."));
- add_cmd ("tracepoints", class_trace,
- _("Tracing of program execution without stopping the program."),
- &cmdlist);
- add_com ("tdump", class_trace, tdump_command,
- _("Print everything collected at the current tracepoint."));
- c = add_com ("tvariable", class_trace, trace_variable_command,_("\
- Define a trace state variable.\n\
- Argument is a $-prefixed name, optionally followed\n\
- by '=' and an expression that sets the initial value\n\
- at the start of tracing."));
- set_cmd_completer (c, expression_completer);
- add_cmd ("tvariable", class_trace, delete_trace_variable_command, _("\
- Delete one or more trace state variables.\n\
- Arguments are the names of the variables to delete.\n\
- If no arguments are supplied, delete all variables."), &deletelist);
- /* FIXME add a trace variable completer. */
- add_info ("tvariables", info_tvariables_command, _("\
- Status of trace state variables and their values."));
- add_info ("static-tracepoint-markers",
- info_static_tracepoint_markers_command, _("\
- List target static tracepoints markers."));
- add_prefix_cmd ("tfind", class_trace, tfind_command, _("\
- Select a trace frame.\n\
- No argument means forward by one frame; '-' means backward by one frame."),
- &tfindlist, 1, &cmdlist);
- add_cmd ("outside", class_trace, tfind_outside_command, _("\
- Select a trace frame whose PC is outside the given range (exclusive).\n\
- Usage: tfind outside ADDR1, ADDR2"),
- &tfindlist);
- add_cmd ("range", class_trace, tfind_range_command, _("\
- Select a trace frame whose PC is in the given range (inclusive).\n\
- Usage: tfind range ADDR1, ADDR2"),
- &tfindlist);
- add_cmd ("line", class_trace, tfind_line_command, _("\
- Select a trace frame by source line.\n\
- Argument can be a line number (with optional source file),\n\
- a function name, or '*' followed by an address.\n\
- Default argument is 'the next source line that was traced'."),
- &tfindlist);
- add_cmd ("tracepoint", class_trace, tfind_tracepoint_command, _("\
- Select a trace frame by tracepoint number.\n\
- Default is the tracepoint for the current trace frame."),
- &tfindlist);
- add_cmd ("pc", class_trace, tfind_pc_command, _("\
- Select a trace frame by PC.\n\
- Default is the current PC, or the PC of the current trace frame."),
- &tfindlist);
- cmd_list_element *tfind_end_cmd
- = add_cmd ("end", class_trace, tfind_end_command, _("\
- De-select any trace frame and resume 'live' debugging."), &tfindlist);
- add_alias_cmd ("none", tfind_end_cmd, class_trace, 0, &tfindlist);
- add_cmd ("start", class_trace, tfind_start_command,
- _("Select the first trace frame in the trace buffer."),
- &tfindlist);
- add_com ("tstatus", class_trace, tstatus_command,
- _("Display the status of the current trace data collection."));
- add_com ("tstop", class_trace, tstop_command, _("\
- Stop trace data collection.\n\
- Usage: tstop [NOTES]...\n\
- Any arguments supplied are recorded with the trace as a stop reason and\n\
- reported by tstatus (if the target supports trace notes)."));
- add_com ("tstart", class_trace, tstart_command, _("\
- Start trace data collection.\n\
- Usage: tstart [NOTES]...\n\
- Any arguments supplied are recorded with the trace as a note and\n\
- reported by tstatus (if the target supports trace notes)."));
- add_com ("end", class_trace, end_actions_pseudocommand, _("\
- Ends a list of commands or actions.\n\
- Several GDB commands allow you to enter a list of commands or actions.\n\
- Entering \"end\" on a line by itself is the normal way to terminate\n\
- such a list.\n\n\
- Note: the \"end\" command cannot be used at the gdb prompt."));
- while_stepping_cmd_element = add_com ("while-stepping", class_trace,
- while_stepping_pseudocommand, _("\
- Specify single-stepping behavior at a tracepoint.\n\
- Argument is number of instructions to trace in single-step mode\n\
- following the tracepoint. This command is normally followed by\n\
- one or more \"collect\" commands, to specify what to collect\n\
- while single-stepping.\n\n\
- Note: this command can only be used in a tracepoint \"actions\" list."));
- add_com_alias ("ws", while_stepping_cmd_element, class_trace, 0);
- add_com_alias ("stepping", while_stepping_cmd_element, class_trace, 0);
- add_com ("collect", class_trace, collect_pseudocommand, _("\
- Specify one or more data items to be collected at a tracepoint.\n\
- Accepts a comma-separated list of (one or more) expressions. GDB will\n\
- collect all data (variables, registers) referenced by that expression.\n\
- Also accepts the following special arguments:\n\
- $regs -- all registers.\n\
- $args -- all function arguments.\n\
- $locals -- all variables local to the block/function scope.\n\
- $_sdata -- static tracepoint data (ignored for non-static tracepoints).\n\
- Note: this command can only be used in a tracepoint \"actions\" list."));
- add_com ("teval", class_trace, teval_pseudocommand, _("\
- Specify one or more expressions to be evaluated at a tracepoint.\n\
- Accepts a comma-separated list of (one or more) expressions.\n\
- The result of each evaluation will be discarded.\n\
- Note: this command can only be used in a tracepoint \"actions\" list."));
- add_com ("actions", class_trace, actions_command, _("\
- Specify the actions to be taken at a tracepoint.\n\
- Tracepoint actions may include collecting of specified data,\n\
- single-stepping, or enabling/disabling other tracepoints,\n\
- depending on target's capabilities."));
- add_setshow_string_cmd ("default-collect", class_trace,
- &default_collect, _("\
- Set the list of expressions to collect by default."), _("\
- Show the list of expressions to collect by default."), NULL,
- NULL, NULL,
- &setlist, &showlist);
- add_setshow_boolean_cmd ("disconnected-tracing", no_class,
- &disconnected_tracing, _("\
- Set whether tracing continues after GDB disconnects."), _("\
- Show whether tracing continues after GDB disconnects."), _("\
- Use this to continue a tracing run even if GDB disconnects\n\
- or detaches from the target. You can reconnect later and look at\n\
- trace data collected in the meantime."),
- set_disconnected_tracing,
- NULL,
- &setlist,
- &showlist);
- add_setshow_boolean_cmd ("circular-trace-buffer", no_class,
- &circular_trace_buffer, _("\
- Set target's use of circular trace buffer."), _("\
- Show target's use of circular trace buffer."), _("\
- Use this to make the trace buffer into a circular buffer,\n\
- which will discard traceframes (oldest first) instead of filling\n\
- up and stopping the trace run."),
- set_circular_trace_buffer,
- NULL,
- &setlist,
- &showlist);
- add_setshow_zuinteger_unlimited_cmd ("trace-buffer-size", no_class,
- &trace_buffer_size, _("\
- Set requested size of trace buffer."), _("\
- Show requested size of trace buffer."), _("\
- Use this to choose a size for the trace buffer. Some targets\n\
- may have fixed or limited buffer sizes. Specifying \"unlimited\" or -1\n\
- disables any attempt to set the buffer size and lets the target choose."),
- set_trace_buffer_size, NULL,
- &setlist, &showlist);
- add_setshow_string_cmd ("trace-user", class_trace,
- &trace_user, _("\
- Set the user name to use for current and future trace runs."), _("\
- Show the user name to use for current and future trace runs."), NULL,
- set_trace_user, NULL,
- &setlist, &showlist);
- add_setshow_string_cmd ("trace-notes", class_trace,
- &trace_notes, _("\
- Set notes string to use for current and future trace runs."), _("\
- Show the notes string to use for current and future trace runs."), NULL,
- set_trace_notes, NULL,
- &setlist, &showlist);
- add_setshow_string_cmd ("trace-stop-notes", class_trace,
- &trace_stop_notes, _("\
- Set notes string to use for future tstop commands."), _("\
- Show the notes string to use for future tstop commands."), NULL,
- set_trace_stop_notes, NULL,
- &setlist, &showlist);
- }
|