Share code on to_xfer_partial for tfile and ctf target
[platform/upstream/binutils.git] / gdb / tracefile.c
1 /* Trace file support in GDB.
2
3    Copyright (C) 1997-2014 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 "ctf.h"
23 #include "exec.h"
24
25 /* Helper macros.  */
26
27 #define TRACE_WRITE_R_BLOCK(writer, buf, size)  \
28   writer->ops->frame_ops->write_r_block ((writer), (buf), (size))
29 #define TRACE_WRITE_M_BLOCK_HEADER(writer, addr, size)            \
30   writer->ops->frame_ops->write_m_block_header ((writer), (addr), \
31                                                 (size))
32 #define TRACE_WRITE_M_BLOCK_MEMORY(writer, buf, size)     \
33   writer->ops->frame_ops->write_m_block_memory ((writer), (buf), \
34                                                 (size))
35 #define TRACE_WRITE_V_BLOCK(writer, num, val)   \
36   writer->ops->frame_ops->write_v_block ((writer), (num), (val))
37
38 /* Free trace file writer.  */
39
40 static void
41 trace_file_writer_xfree (void *arg)
42 {
43   struct trace_file_writer *writer = arg;
44
45   writer->ops->dtor (writer);
46   xfree (writer);
47 }
48
49 /* Save tracepoint data to file named FILENAME through WRITER.  WRITER
50    determines the trace file format.  If TARGET_DOES_SAVE is non-zero,
51    the save is performed on the target, otherwise GDB obtains all trace
52    data and saves it locally.  */
53
54 static void
55 trace_save (const char *filename, struct trace_file_writer *writer,
56             int target_does_save)
57 {
58   struct trace_status *ts = current_trace_status ();
59   int status;
60   struct uploaded_tp *uploaded_tps = NULL, *utp;
61   struct uploaded_tsv *uploaded_tsvs = NULL, *utsv;
62
63   ULONGEST offset = 0;
64 #define MAX_TRACE_UPLOAD 2000
65   gdb_byte buf[MAX_TRACE_UPLOAD];
66   int written;
67   enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ());
68
69   /* If the target is to save the data to a file on its own, then just
70      send the command and be done with it.  */
71   if (target_does_save)
72     {
73       if (!writer->ops->target_save (writer, filename))
74         error (_("Target failed to save trace data to '%s'."),
75                filename);
76       return;
77     }
78
79   /* Get the trace status first before opening the file, so if the
80      target is losing, we can get out without touching files.  */
81   status = target_get_trace_status (ts);
82
83   writer->ops->start (writer, filename);
84
85   writer->ops->write_header (writer);
86
87   /* Write descriptive info.  */
88
89   /* Write out the size of a register block.  */
90   writer->ops->write_regblock_type (writer, trace_regblock_size);
91
92   /* Write out status of the tracing run (aka "tstatus" info).  */
93   writer->ops->write_status (writer, ts);
94
95   /* Note that we want to upload tracepoints and save those, rather
96      than simply writing out the local ones, because the user may have
97      changed tracepoints in GDB in preparation for a future tracing
98      run, or maybe just mass-deleted all types of breakpoints as part
99      of cleaning up.  So as not to contaminate the session, leave the
100      data in its uploaded form, don't make into real tracepoints.  */
101
102   /* Get trace state variables first, they may be checked when parsing
103      uploaded commands.  */
104
105   target_upload_trace_state_variables (&uploaded_tsvs);
106
107   for (utsv = uploaded_tsvs; utsv; utsv = utsv->next)
108     writer->ops->write_uploaded_tsv (writer, utsv);
109
110   free_uploaded_tsvs (&uploaded_tsvs);
111
112   target_upload_tracepoints (&uploaded_tps);
113
114   for (utp = uploaded_tps; utp; utp = utp->next)
115     target_get_tracepoint_status (NULL, utp);
116
117   for (utp = uploaded_tps; utp; utp = utp->next)
118     writer->ops->write_uploaded_tp (writer, utp);
119
120   free_uploaded_tps (&uploaded_tps);
121
122   /* Mark the end of the definition section.  */
123   writer->ops->write_definition_end (writer);
124
125   /* Get and write the trace data proper.  */
126   while (1)
127     {
128       LONGEST gotten = 0;
129
130       /* The writer supports writing the contents of trace buffer
131           directly to trace file.  Don't parse the contents of trace
132           buffer.  */
133       if (writer->ops->write_trace_buffer != NULL)
134         {
135           /* We ask for big blocks, in the hopes of efficiency, but
136              will take less if the target has packet size limitations
137              or some such.  */
138           gotten = target_get_raw_trace_data (buf, offset,
139                                               MAX_TRACE_UPLOAD);
140           if (gotten < 0)
141             error (_("Failure to get requested trace buffer data"));
142           /* No more data is forthcoming, we're done.  */
143           if (gotten == 0)
144             break;
145
146           writer->ops->write_trace_buffer (writer, buf, gotten);
147
148           offset += gotten;
149         }
150       else
151         {
152           uint16_t tp_num;
153           uint32_t tf_size;
154           /* Parse the trace buffers according to how data are stored
155              in trace buffer in GDBserver.  */
156
157           gotten = target_get_raw_trace_data (buf, offset, 6);
158
159           if (gotten == 0)
160             break;
161
162           /* Read the first six bytes in, which is the tracepoint
163              number and trace frame size.  */
164           tp_num = (uint16_t)
165             extract_unsigned_integer (&buf[0], 2, byte_order);
166
167           tf_size = (uint32_t)
168             extract_unsigned_integer (&buf[2], 4, byte_order);
169
170           writer->ops->frame_ops->start (writer, tp_num);
171           gotten = 6;
172
173           if (tf_size > 0)
174             {
175               unsigned int block;
176
177               offset += 6;
178
179               for (block = 0; block < tf_size; )
180                 {
181                   gdb_byte block_type;
182
183                   /* We'll fetch one block each time, in order to
184                      handle the extremely large 'M' block.  We first
185                      fetch one byte to get the type of the block.  */
186                   gotten = target_get_raw_trace_data (buf, offset, 1);
187                   if (gotten < 1)
188                     error (_("Failure to get requested trace buffer data"));
189
190                   gotten = 1;
191                   block += 1;
192                   offset += 1;
193
194                   block_type = buf[0];
195                   switch (block_type)
196                     {
197                     case 'R':
198                       gotten
199                         = target_get_raw_trace_data (buf, offset,
200                                                      trace_regblock_size);
201                       if (gotten < trace_regblock_size)
202                         error (_("Failure to get requested trace"
203                                  " buffer data"));
204
205                       TRACE_WRITE_R_BLOCK (writer, buf,
206                                            trace_regblock_size);
207                       break;
208                     case 'M':
209                       {
210                         unsigned short mlen;
211                         ULONGEST addr;
212                         LONGEST t;
213                         int j;
214
215                         t = target_get_raw_trace_data (buf,offset, 10);
216                         if (t < 10)
217                           error (_("Failure to get requested trace"
218                                    " buffer data"));
219
220                         offset += 10;
221                         block += 10;
222
223                         gotten = 0;
224                         addr = (ULONGEST)
225                           extract_unsigned_integer (buf, 8,
226                                                     byte_order);
227                         mlen = (unsigned short)
228                           extract_unsigned_integer (&buf[8], 2,
229                                                     byte_order);
230
231                         TRACE_WRITE_M_BLOCK_HEADER (writer, addr,
232                                                     mlen);
233
234                         /* The memory contents in 'M' block may be
235                            very large.  Fetch the data from the target
236                            and write them into file one by one.  */
237                         for (j = 0; j < mlen; )
238                           {
239                             unsigned int read_length;
240
241                             if (mlen - j > MAX_TRACE_UPLOAD)
242                               read_length = MAX_TRACE_UPLOAD;
243                             else
244                               read_length = mlen - j;
245
246                             t = target_get_raw_trace_data (buf,
247                                                            offset + j,
248                                                            read_length);
249                             if (t < read_length)
250                               error (_("Failure to get requested"
251                                        " trace buffer data"));
252
253                             TRACE_WRITE_M_BLOCK_MEMORY (writer, buf,
254                                                         read_length);
255
256                             j += read_length;
257                             gotten += read_length;
258                           }
259
260                         break;
261                       }
262                     case 'V':
263                       {
264                         int vnum;
265                         LONGEST val;
266
267                         gotten
268                           = target_get_raw_trace_data (buf, offset,
269                                                        12);
270                         if (gotten < 12)
271                           error (_("Failure to get requested"
272                                    " trace buffer data"));
273
274                         vnum  = (int) extract_signed_integer (buf,
275                                                               4,
276                                                               byte_order);
277                         val
278                           = extract_signed_integer (&buf[4], 8,
279                                                     byte_order);
280
281                         TRACE_WRITE_V_BLOCK (writer, vnum, val);
282                       }
283                       break;
284                     default:
285                       error (_("Unknown block type '%c' (0x%x) in"
286                                " trace frame"),
287                              block_type, block_type);
288                     }
289
290                   block += gotten;
291                   offset += gotten;
292                 }
293             }
294           else
295             offset += gotten;
296
297           writer->ops->frame_ops->end (writer);
298         }
299     }
300
301   writer->ops->end (writer);
302 }
303
304 static void
305 trace_save_command (char *args, int from_tty)
306 {
307   int target_does_save = 0;
308   char **argv;
309   char *filename = NULL;
310   struct cleanup *back_to;
311   int generate_ctf = 0;
312   struct trace_file_writer *writer = NULL;
313
314   if (args == NULL)
315     error_no_arg (_("file in which to save trace data"));
316
317   argv = gdb_buildargv (args);
318   back_to = make_cleanup_freeargv (argv);
319
320   for (; *argv; ++argv)
321     {
322       if (strcmp (*argv, "-r") == 0)
323         target_does_save = 1;
324       if (strcmp (*argv, "-ctf") == 0)
325         generate_ctf = 1;
326       else if (**argv == '-')
327         error (_("unknown option `%s'"), *argv);
328       else
329         filename = *argv;
330     }
331
332   if (!filename)
333     error_no_arg (_("file in which to save trace data"));
334
335   if (generate_ctf)
336     writer = ctf_trace_file_writer_new ();
337   else
338     writer = tfile_trace_file_writer_new ();
339
340   make_cleanup (trace_file_writer_xfree, writer);
341
342   trace_save (filename, writer, target_does_save);
343
344   if (from_tty)
345     printf_filtered (_("Trace data saved to %s '%s'.\n"),
346                      generate_ctf ? "directory" : "file", filename);
347
348   do_cleanups (back_to);
349 }
350
351 /* Save the trace data to file FILENAME of tfile format.  */
352
353 void
354 trace_save_tfile (const char *filename, int target_does_save)
355 {
356   struct trace_file_writer *writer;
357   struct cleanup *back_to;
358
359   writer = tfile_trace_file_writer_new ();
360   back_to = make_cleanup (trace_file_writer_xfree, writer);
361   trace_save (filename, writer, target_does_save);
362   do_cleanups (back_to);
363 }
364
365 /* Save the trace data to dir DIRNAME of ctf format.  */
366
367 void
368 trace_save_ctf (const char *dirname, int target_does_save)
369 {
370   struct trace_file_writer *writer;
371   struct cleanup *back_to;
372
373   writer = ctf_trace_file_writer_new ();
374   back_to = make_cleanup (trace_file_writer_xfree, writer);
375
376   trace_save (dirname, writer, target_does_save);
377   do_cleanups (back_to);
378 }
379
380 /* This is the implementation of target_ops method to_has_all_memory.  */
381
382 static int
383 tracefile_has_all_memory (struct target_ops *ops)
384 {
385   return 1;
386 }
387
388 /* This is the implementation of target_ops method to_has_memory.  */
389
390 static int
391 tracefile_has_memory (struct target_ops *ops)
392 {
393   return 1;
394 }
395
396 /* This is the implementation of target_ops method to_has_stack.
397    The target has a stack when GDB has already selected one trace
398    frame.  */
399
400 static int
401 tracefile_has_stack (struct target_ops *ops)
402 {
403   return get_traceframe_number () != -1;
404 }
405
406 /* This is the implementation of target_ops method to_has_registers.
407    The target has registers when GDB has already selected one trace
408    frame.  */
409
410 static int
411 tracefile_has_registers (struct target_ops *ops)
412 {
413   return get_traceframe_number () != -1;
414 }
415
416 /* This is the implementation of target_ops method to_thread_alive.
417    tracefile has one thread faked by GDB.  */
418
419 static int
420 tracefile_thread_alive (struct target_ops *ops, ptid_t ptid)
421 {
422   return 1;
423 }
424
425 /* This is the implementation of target_ops method to_get_trace_status.
426    The trace status for a file is that tracing can never be run.  */
427
428 static int
429 tracefile_get_trace_status (struct target_ops *self, struct trace_status *ts)
430 {
431   /* Other bits of trace status were collected as part of opening the
432      trace files, so nothing to do here.  */
433
434   return -1;
435 }
436
437 /* Initialize OPS for tracefile related targets.  */
438
439 void
440 init_tracefile_ops (struct target_ops *ops)
441 {
442   ops->to_stratum = process_stratum;
443   ops->to_get_trace_status = tracefile_get_trace_status;
444   ops->to_has_all_memory = tracefile_has_all_memory;
445   ops->to_has_memory = tracefile_has_memory;
446   ops->to_has_stack = tracefile_has_stack;
447   ops->to_has_registers = tracefile_has_registers;
448   ops->to_thread_alive = tracefile_thread_alive;
449   ops->to_magic = OPS_MAGIC;
450 }
451
452 extern initialize_file_ftype _initialize_tracefile;
453
454 void
455 _initialize_tracefile (void)
456 {
457   add_com ("tsave", class_trace, trace_save_command, _("\
458 Save the trace data to a file.\n\
459 Use the '-ctf' option to save the data to CTF format.\n\
460 Use the '-r' option to direct the target to save directly to the file,\n\
461 using its own filesystem."));
462 }