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