xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/tracefile.c (revision 8b657b0747480f8989760d71343d6dd33f8d4cf9)
1 /* Trace file support in GDB.
2 
3    Copyright (C) 1997-2023 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "defs.h"
21 #include "tracefile.h"
22 #include "tracectf.h"
23 #include "exec.h"
24 #include "regcache.h"
25 #include "gdbsupport/byte-vector.h"
26 #include "gdbarch.h"
27 #include "gdbsupport/buildargv.h"
28 
29 /* Helper macros.  */
30 
31 #define TRACE_WRITE_R_BLOCK(writer, buf, size)	\
32   writer->ops->frame_ops->write_r_block ((writer), (buf), (size))
33 #define TRACE_WRITE_M_BLOCK_HEADER(writer, addr, size)		  \
34   writer->ops->frame_ops->write_m_block_header ((writer), (addr), \
35 						(size))
36 #define TRACE_WRITE_M_BLOCK_MEMORY(writer, buf, size)	  \
37   writer->ops->frame_ops->write_m_block_memory ((writer), (buf), \
38 						(size))
39 #define TRACE_WRITE_V_BLOCK(writer, num, val)	\
40   writer->ops->frame_ops->write_v_block ((writer), (num), (val))
41 
42 /* A unique pointer policy class for trace_file_writer.  */
43 
44 struct trace_file_writer_deleter
45 {
46   void operator() (struct trace_file_writer *writer)
47   {
48     writer->ops->dtor (writer);
49     xfree (writer);
50   }
51 };
52 
53 /* A unique_ptr specialization for trace_file_writer.  */
54 
55 typedef std::unique_ptr<trace_file_writer, trace_file_writer_deleter>
56     trace_file_writer_up;
57 
58 /* Save tracepoint data to file named FILENAME through WRITER.  WRITER
59    determines the trace file format.  If TARGET_DOES_SAVE is non-zero,
60    the save is performed on the target, otherwise GDB obtains all trace
61    data and saves it locally.  */
62 
63 static void
64 trace_save (const char *filename, struct trace_file_writer *writer,
65 	    int target_does_save)
66 {
67   struct trace_status *ts = current_trace_status ();
68   struct uploaded_tp *uploaded_tps = NULL, *utp;
69   struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
70 
71   ULONGEST offset = 0;
72 #define MAX_TRACE_UPLOAD 2000
73   gdb::byte_vector buf (std::max (MAX_TRACE_UPLOAD, trace_regblock_size));
74   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
75 
76   /* If the target is to save the data to a file on its own, then just
77      send the command and be done with it.  */
78   if (target_does_save)
79     {
80       if (!writer->ops->target_save (writer, filename))
81 	error (_("Target failed to save trace data to '%s'."),
82 	       filename);
83       return;
84     }
85 
86   /* Get the trace status first before opening the file, so if the
87      target is losing, we can get out without touching files.  Since
88      we're just calling this for side effects, we ignore the
89      result.  */
90   target_get_trace_status (ts);
91 
92   writer->ops->start (writer, filename);
93 
94   writer->ops->write_header (writer);
95 
96   /* Write descriptive info.  */
97 
98   /* Write out the size of a register block.  */
99   writer->ops->write_regblock_type (writer, trace_regblock_size);
100 
101   /* Write out the target description info.  */
102   writer->ops->write_tdesc (writer);
103 
104   /* Write out status of the tracing run (aka "tstatus" info).  */
105   writer->ops->write_status (writer, ts);
106 
107   /* Note that we want to upload tracepoints and save those, rather
108      than simply writing out the local ones, because the user may have
109      changed tracepoints in GDB in preparation for a future tracing
110      run, or maybe just mass-deleted all types of breakpoints as part
111      of cleaning up.  So as not to contaminate the session, leave the
112      data in its uploaded form, don't make into real tracepoints.  */
113 
114   /* Get trace state variables first, they may be checked when parsing
115      uploaded commands.  */
116 
117   target_upload_trace_state_variables (&uploaded_tsvs);
118 
119   for (utsv = uploaded_tsvs; utsv; utsv = utsv->next)
120     writer->ops->write_uploaded_tsv (writer, utsv);
121 
122   free_uploaded_tsvs (&uploaded_tsvs);
123 
124   target_upload_tracepoints (&uploaded_tps);
125 
126   for (utp = uploaded_tps; utp; utp = utp->next)
127     target_get_tracepoint_status (NULL, utp);
128 
129   for (utp = uploaded_tps; utp; utp = utp->next)
130     writer->ops->write_uploaded_tp (writer, utp);
131 
132   free_uploaded_tps (&uploaded_tps);
133 
134   /* Mark the end of the definition section.  */
135   writer->ops->write_definition_end (writer);
136 
137   /* Get and write the trace data proper.  */
138   while (1)
139     {
140       LONGEST gotten = 0;
141 
142       /* The writer supports writing the contents of trace buffer
143 	  directly to trace file.  Don't parse the contents of trace
144 	  buffer.  */
145       if (writer->ops->write_trace_buffer != NULL)
146 	{
147 	  /* We ask for big blocks, in the hopes of efficiency, but
148 	     will take less if the target has packet size limitations
149 	     or some such.  */
150 	  gotten = target_get_raw_trace_data (buf.data (), offset,
151 					      MAX_TRACE_UPLOAD);
152 	  if (gotten < 0)
153 	    error (_("Failure to get requested trace buffer data"));
154 	  /* No more data is forthcoming, we're done.  */
155 	  if (gotten == 0)
156 	    break;
157 
158 	  writer->ops->write_trace_buffer (writer, buf.data (), gotten);
159 
160 	  offset += gotten;
161 	}
162       else
163 	{
164 	  uint16_t tp_num;
165 	  uint32_t tf_size;
166 	  /* Parse the trace buffers according to how data are stored
167 	     in trace buffer in GDBserver.  */
168 
169 	  gotten = target_get_raw_trace_data (buf.data (), offset, 6);
170 
171 	  if (gotten == 0)
172 	    break;
173 
174 	  /* Read the first six bytes in, which is the tracepoint
175 	     number and trace frame size.  */
176 	  tp_num = (uint16_t)
177 	    extract_unsigned_integer (&((buf.data ())[0]), 2, byte_order);
178 
179 	  tf_size = (uint32_t)
180 	    extract_unsigned_integer (&((buf.data ())[2]), 4, byte_order);
181 
182 	  writer->ops->frame_ops->start (writer, tp_num);
183 	  gotten = 6;
184 
185 	  if (tf_size > 0)
186 	    {
187 	      unsigned int block;
188 
189 	      offset += 6;
190 
191 	      for (block = 0; block < tf_size; )
192 		{
193 		  gdb_byte block_type;
194 
195 		  /* We'll fetch one block each time, in order to
196 		     handle the extremely large 'M' block.  We first
197 		     fetch one byte to get the type of the block.  */
198 		  gotten = target_get_raw_trace_data (buf.data (),
199 						      offset, 1);
200 		  if (gotten < 1)
201 		    error (_("Failure to get requested trace buffer data"));
202 
203 		  gotten = 1;
204 		  block += 1;
205 		  offset += 1;
206 
207 		  block_type = buf[0];
208 		  switch (block_type)
209 		    {
210 		    case 'R':
211 		      gotten
212 			= target_get_raw_trace_data (buf.data (), offset,
213 						     trace_regblock_size);
214 		      if (gotten < trace_regblock_size)
215 			error (_("Failure to get requested trace"
216 				 " buffer data"));
217 
218 		      TRACE_WRITE_R_BLOCK (writer, buf.data (),
219 					   trace_regblock_size);
220 		      break;
221 		    case 'M':
222 		      {
223 			unsigned short mlen;
224 			ULONGEST addr;
225 			LONGEST t;
226 			int j;
227 
228 			t = target_get_raw_trace_data (buf.data (),
229 						       offset, 10);
230 			if (t < 10)
231 			  error (_("Failure to get requested trace"
232 				   " buffer data"));
233 
234 			offset += 10;
235 			block += 10;
236 
237 			gotten = 0;
238 			addr = (ULONGEST)
239 			  extract_unsigned_integer (buf.data (), 8,
240 						    byte_order);
241 			mlen = (unsigned short)
242 			  extract_unsigned_integer (&((buf.data ())[8]), 2,
243 						    byte_order);
244 
245 			TRACE_WRITE_M_BLOCK_HEADER (writer, addr,
246 						    mlen);
247 
248 			/* The memory contents in 'M' block may be
249 			   very large.  Fetch the data from the target
250 			   and write them into file one by one.  */
251 			for (j = 0; j < mlen; )
252 			  {
253 			    unsigned int read_length;
254 
255 			    if (mlen - j > MAX_TRACE_UPLOAD)
256 			      read_length = MAX_TRACE_UPLOAD;
257 			    else
258 			      read_length = mlen - j;
259 
260 			    t = target_get_raw_trace_data (buf.data (),
261 							   offset + j,
262 							   read_length);
263 			    if (t < read_length)
264 			      error (_("Failure to get requested"
265 				       " trace buffer data"));
266 
267 			    TRACE_WRITE_M_BLOCK_MEMORY (writer,
268 							buf.data (),
269 							read_length);
270 
271 			    j += read_length;
272 			    gotten += read_length;
273 			  }
274 
275 			break;
276 		      }
277 		    case 'V':
278 		      {
279 			int vnum;
280 			LONGEST val;
281 
282 			gotten
283 			  = target_get_raw_trace_data (buf.data (),
284 						       offset, 12);
285 			if (gotten < 12)
286 			  error (_("Failure to get requested"
287 				   " trace buffer data"));
288 
289 			vnum  = (int) extract_signed_integer (buf.data (),
290 							      4,
291 							      byte_order);
292 			val
293 			  = extract_signed_integer (&((buf.data ())[4]),
294 						    8, byte_order);
295 
296 			TRACE_WRITE_V_BLOCK (writer, vnum, val);
297 		      }
298 		      break;
299 		    default:
300 		      error (_("Unknown block type '%c' (0x%x) in"
301 			       " trace frame"),
302 			     block_type, block_type);
303 		    }
304 
305 		  block += gotten;
306 		  offset += gotten;
307 		}
308 	    }
309 	  else
310 	    offset += gotten;
311 
312 	  writer->ops->frame_ops->end (writer);
313 	}
314     }
315 
316   writer->ops->end (writer);
317 }
318 
319 static void
320 tsave_command (const char *args, int from_tty)
321 {
322   int target_does_save = 0;
323   char **argv;
324   char *filename = NULL;
325   int generate_ctf = 0;
326 
327   if (args == NULL)
328     error_no_arg (_("file in which to save trace data"));
329 
330   gdb_argv built_argv (args);
331   argv = built_argv.get ();
332 
333   for (; *argv; ++argv)
334     {
335       if (strcmp (*argv, "-r") == 0)
336 	target_does_save = 1;
337       else if (strcmp (*argv, "-ctf") == 0)
338 	generate_ctf = 1;
339       else if (**argv == '-')
340 	error (_("unknown option `%s'"), *argv);
341       else
342 	filename = *argv;
343     }
344 
345   if (!filename)
346     error_no_arg (_("file in which to save trace data"));
347 
348   if (generate_ctf)
349     trace_save_ctf (filename, target_does_save);
350   else
351     trace_save_tfile (filename, target_does_save);
352 
353   if (from_tty)
354     gdb_printf (_("Trace data saved to %s '%s'.\n"),
355 		generate_ctf ? "directory" : "file", filename);
356 }
357 
358 /* Save the trace data to file FILENAME of tfile format.  */
359 
360 void
361 trace_save_tfile (const char *filename, int target_does_save)
362 {
363   trace_file_writer_up writer (tfile_trace_file_writer_new ());
364   trace_save (filename, writer.get (), target_does_save);
365 }
366 
367 /* Save the trace data to dir DIRNAME of ctf format.  */
368 
369 void
370 trace_save_ctf (const char *dirname, int target_does_save)
371 {
372   trace_file_writer_up writer (ctf_trace_file_writer_new ());
373   trace_save (dirname, writer.get (), target_does_save);
374 }
375 
376 /* Fetch register data from tracefile, shared for both tfile and
377    ctf.  */
378 
379 void
380 tracefile_fetch_registers (struct regcache *regcache, int regno)
381 {
382   struct gdbarch *gdbarch = regcache->arch ();
383   struct tracepoint *tp = get_tracepoint (get_tracepoint_number ());
384   int regn;
385 
386   /* We get here if no register data has been found.  Mark registers
387      as unavailable.  */
388   for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++)
389     regcache->raw_supply (regn, NULL);
390 
391   /* We can often usefully guess that the PC is going to be the same
392      as the address of the tracepoint.  */
393   if (tp == NULL || tp->loc == NULL)
394     return;
395 
396   /* But don't try to guess if tracepoint is multi-location...  */
397   if (tp->loc->next)
398     {
399       warning (_("Tracepoint %d has multiple "
400 		 "locations, cannot infer $pc"),
401 	       tp->number);
402       return;
403     }
404   /* ... or does while-stepping.  */
405   else if (tp->step_count > 0)
406     {
407       warning (_("Tracepoint %d does while-stepping, "
408 		 "cannot infer $pc"),
409 	       tp->number);
410       return;
411     }
412 
413   /* Guess what we can from the tracepoint location.  */
414   gdbarch_guess_tracepoint_registers (gdbarch, regcache,
415 				      tp->loc->address);
416 }
417 
418 /* This is the implementation of target_ops method to_has_all_memory.  */
419 
420 bool
421 tracefile_target::has_all_memory ()
422 {
423   return true;
424 }
425 
426 /* This is the implementation of target_ops method to_has_memory.  */
427 
428 bool
429 tracefile_target::has_memory ()
430 {
431   return true;
432 }
433 
434 /* This is the implementation of target_ops method to_has_stack.
435    The target has a stack when GDB has already selected one trace
436    frame.  */
437 
438 bool
439 tracefile_target::has_stack ()
440 {
441   return get_traceframe_number () != -1;
442 }
443 
444 /* This is the implementation of target_ops method to_has_registers.
445    The target has registers when GDB has already selected one trace
446    frame.  */
447 
448 bool
449 tracefile_target::has_registers ()
450 {
451   return get_traceframe_number () != -1;
452 }
453 
454 /* This is the implementation of target_ops method to_thread_alive.
455    tracefile has one thread faked by GDB.  */
456 
457 bool
458 tracefile_target::thread_alive (ptid_t ptid)
459 {
460   return true;
461 }
462 
463 /* This is the implementation of target_ops method to_get_trace_status.
464    The trace status for a file is that tracing can never be run.  */
465 
466 int
467 tracefile_target::get_trace_status (struct trace_status *ts)
468 {
469   /* Other bits of trace status were collected as part of opening the
470      trace files, so nothing to do here.  */
471 
472   return -1;
473 }
474 
475 void _initialize_tracefile ();
476 void
477 _initialize_tracefile ()
478 {
479   add_com ("tsave", class_trace, tsave_command, _("\
480 Save the trace data to a file.\n\
481 Use the '-ctf' option to save the data to CTF format.\n\
482 Use the '-r' option to direct the target to save directly to the file,\n\
483 using its own filesystem."));
484 }
485