libctf, types: enhance ctf_type_aname to print function arg types
[deliverable/binutils-gdb.git] / libctf / ctf-dump.c
1 /* Textual dumping of CTF data.
2 Copyright (C) 2019-2020 Free Software Foundation, Inc.
3
4 This file is part of libctf.
5
6 libctf is free software; you can redistribute it and/or modify it under
7 the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 3, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; see the file COPYING. If not see
18 <http://www.gnu.org/licenses/>. */
19
20 #include <ctf-impl.h>
21 #include <string.h>
22
23 #define str_append(s, a) ctf_str_append_noerr (s, a)
24
25 /* One item to be dumped, in string form. */
26
27 typedef struct ctf_dump_item
28 {
29 ctf_list_t cdi_list;
30 char *cdi_item;
31 } ctf_dump_item_t;
32
33 /* Cross-call state for dumping. Basically just enough to track the section in
34 use and a list of return strings. */
35
36 struct ctf_dump_state
37 {
38 ctf_sect_names_t cds_sect;
39 ctf_file_t *cds_fp;
40 ctf_dump_item_t *cds_current;
41 ctf_list_t cds_items;
42 };
43
44 /* Cross-call state for ctf_dump_member. */
45
46 typedef struct ctf_dump_membstate
47 {
48 char **cdm_str;
49 ctf_file_t *cdm_fp;
50 } ctf_dump_membstate_t;
51
52 static int
53 ctf_dump_append (ctf_dump_state_t *state, char *str)
54 {
55 ctf_dump_item_t *cdi;
56
57 if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
58 return (ctf_set_errno (state->cds_fp, ENOMEM));
59
60 cdi->cdi_item = str;
61 ctf_list_append (&state->cds_items, cdi);
62 return 0;
63 }
64
65 static void
66 ctf_dump_free (ctf_dump_state_t *state)
67 {
68 ctf_dump_item_t *cdi, *next_cdi;
69
70 if (state == NULL)
71 return;
72
73 for (cdi = ctf_list_next (&state->cds_items); cdi != NULL;
74 cdi = next_cdi)
75 {
76 free (cdi->cdi_item);
77 next_cdi = ctf_list_next (cdi);
78 free (cdi);
79 }
80 }
81
82 /* Slices need special handling to distinguish them from their referenced
83 type. */
84
85 static int
86 ctf_is_slice (ctf_file_t *fp, ctf_id_t id, ctf_encoding_t *enc)
87 {
88 int kind = ctf_type_kind (fp, id);
89
90 return (((kind == CTF_K_INTEGER) || (kind == CTF_K_ENUM)
91 || (kind == CTF_K_FLOAT))
92 && ctf_type_reference (fp, id) != CTF_ERR
93 && ctf_type_encoding (fp, id, enc) == 0);
94 }
95
96 /* Return a dump for a single type, without member info: but do show the
97 type's references. */
98
99 static char *
100 ctf_dump_format_type (ctf_file_t *fp, ctf_id_t id, int flag)
101 {
102 ctf_id_t new_id;
103 char *str = NULL, *bit = NULL, *buf = NULL;
104
105 new_id = id;
106 do
107 {
108 ctf_encoding_t enc;
109 const char *nonroot_leader = "";
110 const char *nonroot_trailer = "";
111
112 id = new_id;
113 if (flag == CTF_ADD_NONROOT)
114 {
115 nonroot_leader = "{";
116 nonroot_trailer = "}";
117 }
118
119 buf = ctf_type_aname (fp, id);
120 if (!buf)
121 {
122 if (id == 0 || ctf_errno (fp) == ECTF_NONREPRESENTABLE)
123 {
124 str = str_append (str, " (type not represented in CTF)");
125 ctf_set_errno (fp, ECTF_NOTREF);
126 break;
127 }
128
129 goto err;
130 }
131
132 /* Slices get a different print representation. */
133
134 if (ctf_is_slice (fp, id, &enc))
135 {
136 ctf_type_encoding (fp, id, &enc);
137 if (asprintf (&bit, " %s%lx: [slice 0x%x:0x%x]%s",
138 nonroot_leader, id, enc.cte_offset, enc.cte_bits,
139 nonroot_trailer) < 0)
140 goto oom;
141 }
142 else
143 {
144 if (asprintf (&bit, " %s%lx: %s (size 0x%lx)%s", nonroot_leader,
145 id, buf[0] == '\0' ? "(nameless)" : buf,
146 (unsigned long) ctf_type_size (fp, id),
147 nonroot_trailer) < 0)
148 goto oom;
149 }
150 free (buf);
151 buf = NULL;
152 str = str_append (str, bit);
153 free (bit);
154 bit = NULL;
155
156 new_id = ctf_type_reference (fp, id);
157 if (new_id != CTF_ERR)
158 str = str_append (str, " ->");
159 } while (new_id != CTF_ERR);
160
161 if (ctf_errno (fp) != ECTF_NOTREF)
162 {
163 free (str);
164 return NULL;
165 }
166
167 return str;
168
169 oom:
170 ctf_set_errno (fp, errno);
171 err:
172 free (buf);
173 free (str);
174 free (bit);
175 return NULL;
176 }
177
178 /* Dump one string field from the file header into the cds_items. */
179 static int
180 ctf_dump_header_strfield (ctf_file_t *fp, ctf_dump_state_t *state,
181 const char *name, uint32_t value)
182 {
183 char *str;
184 if (value)
185 {
186 if (asprintf (&str, "%s: %s\n", name, ctf_strptr (fp, value)) < 0)
187 goto err;
188 ctf_dump_append (state, str);
189 }
190 return 0;
191
192 err:
193 return (ctf_set_errno (fp, errno));
194 }
195
196 /* Dump one section-offset field from the file header into the cds_items. */
197 static int
198 ctf_dump_header_sectfield (ctf_file_t *fp, ctf_dump_state_t *state,
199 const char *sect, uint32_t off, uint32_t nextoff)
200 {
201 char *str;
202 if (nextoff - off)
203 {
204 if (asprintf (&str, "%s:\t0x%lx -- 0x%lx (0x%lx bytes)\n", sect,
205 (unsigned long) off, (unsigned long) (nextoff - 1),
206 (unsigned long) (nextoff - off)) < 0)
207 goto err;
208 ctf_dump_append (state, str);
209 }
210 return 0;
211
212 err:
213 return (ctf_set_errno (fp, errno));
214 }
215
216 /* Dump the file header into the cds_items. */
217 static int
218 ctf_dump_header (ctf_file_t *fp, ctf_dump_state_t *state)
219 {
220 char *str;
221 const ctf_header_t *hp = fp->ctf_header;
222 const char *vertab[] =
223 {
224 NULL, "CTF_VERSION_1",
225 "CTF_VERSION_1_UPGRADED_3 (latest format, version 1 type "
226 "boundaries)",
227 "CTF_VERSION_2",
228 "CTF_VERSION_3", NULL
229 };
230 const char *verstr = NULL;
231
232 if (asprintf (&str, "Magic number: %x\n", hp->cth_magic) < 0)
233 goto err;
234 ctf_dump_append (state, str);
235
236 if (hp->cth_version <= CTF_VERSION)
237 verstr = vertab[hp->cth_version];
238
239 if (verstr == NULL)
240 verstr = "(not a valid version)";
241
242 if (asprintf (&str, "Version: %i (%s)\n", hp->cth_version,
243 verstr) < 0)
244 goto err;
245 ctf_dump_append (state, str);
246
247 /* Everything else is only printed if present. */
248
249 /* The flags are unusual in that they represent the ctf_file_t *in memory*:
250 flags representing compression, etc, are turned off as the file is
251 decompressed. So we store a copy of the flags before they are changed, for
252 the dumper. */
253
254 if (fp->ctf_openflags > 0)
255 {
256 if (fp->ctf_openflags)
257 if (asprintf (&str, "Flags: 0x%x (%s)", fp->ctf_openflags,
258 fp->ctf_openflags & CTF_F_COMPRESS ? "CTF_F_COMPRESS"
259 : "") < 0)
260 goto err;
261 ctf_dump_append (state, str);
262 }
263
264 if (ctf_dump_header_strfield (fp, state, "Parent label",
265 hp->cth_parlabel) < 0)
266 goto err;
267
268 if (ctf_dump_header_strfield (fp, state, "Parent name", hp->cth_parname) < 0)
269 goto err;
270
271 if (ctf_dump_header_strfield (fp, state, "Compilation unit name",
272 hp->cth_cuname) < 0)
273 goto err;
274
275 if (ctf_dump_header_sectfield (fp, state, "Label section", hp->cth_lbloff,
276 hp->cth_objtoff) < 0)
277 goto err;
278
279 if (ctf_dump_header_sectfield (fp, state, "Data object section",
280 hp->cth_objtoff, hp->cth_funcoff) < 0)
281 goto err;
282
283 if (ctf_dump_header_sectfield (fp, state, "Function info section",
284 hp->cth_funcoff, hp->cth_varoff) < 0)
285 goto err;
286
287 if (ctf_dump_header_sectfield (fp, state, "Variable section",
288 hp->cth_varoff, hp->cth_typeoff) < 0)
289 goto err;
290
291 if (ctf_dump_header_sectfield (fp, state, "Type section",
292 hp->cth_typeoff, hp->cth_stroff) < 0)
293 goto err;
294
295 if (ctf_dump_header_sectfield (fp, state, "String section", hp->cth_stroff,
296 hp->cth_stroff + hp->cth_strlen + 1) < 0)
297 goto err;
298
299 return 0;
300 err:
301 return (ctf_set_errno (fp, errno));
302 }
303
304 /* Dump a single label into the cds_items. */
305
306 static int
307 ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
308 void *arg)
309 {
310 char *str;
311 char *typestr;
312 ctf_dump_state_t *state = arg;
313
314 if (asprintf (&str, "%s -> ", name) < 0)
315 return (ctf_set_errno (state->cds_fp, errno));
316
317 if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
318 CTF_ADD_ROOT)) == NULL)
319 {
320 free (str);
321 return -1; /* errno is set for us. */
322 }
323
324 str = str_append (str, typestr);
325 free (typestr);
326
327 ctf_dump_append (state, str);
328 return 0;
329 }
330
331 /* Dump all the object entries into the cds_items. (There is no iterator for
332 this section, so we just do it in a loop, and this function handles all of
333 them, rather than only one. */
334
335 static int
336 ctf_dump_objts (ctf_file_t *fp, ctf_dump_state_t *state)
337 {
338 size_t i;
339
340 for (i = 0; i < fp->ctf_nsyms; i++)
341 {
342 char *str;
343 char *typestr;
344 const char *sym_name;
345 ctf_id_t type;
346
347 if ((type = ctf_lookup_by_symbol (state->cds_fp, i)) == CTF_ERR)
348 switch (ctf_errno (state->cds_fp))
349 {
350 /* Most errors are just an indication that this symbol is not a data
351 symbol, but this one indicates that we were called wrong, on a
352 CTF file with no associated symbol table. */
353 case ECTF_NOSYMTAB:
354 return -1;
355 case ECTF_NOTDATA:
356 case ECTF_NOTYPEDAT:
357 continue;
358 }
359
360 /* Variable name. */
361 sym_name = ctf_lookup_symbol_name (fp, i);
362 if (sym_name[0] == '\0')
363 {
364 if (asprintf (&str, "%lx -> ", (unsigned long) i) < 0)
365 return (ctf_set_errno (fp, errno));
366 }
367 else
368 {
369 if (asprintf (&str, "%s (%lx) -> ", sym_name, (unsigned long) i) < 0)
370 return (ctf_set_errno (fp, errno));
371 }
372
373 /* Variable type. */
374 if ((typestr = ctf_dump_format_type (state->cds_fp, type,
375 CTF_ADD_ROOT)) == NULL)
376 {
377 free (str);
378 return -1; /* errno is set for us. */
379 }
380
381 str = str_append (str, typestr);
382 free (typestr);
383
384 ctf_dump_append (state, str);
385 }
386 return 0;
387 }
388
389 /* Dump all the function entries into the cds_items. (As above, there is no
390 iterator for this section.) */
391
392 static int
393 ctf_dump_funcs (ctf_file_t *fp, ctf_dump_state_t *state)
394 {
395 size_t i;
396
397 for (i = 0; i < fp->ctf_nsyms; i++)
398 {
399 char *str;
400 char *bit = NULL;
401 const char *err;
402 const char *sym_name;
403 ctf_funcinfo_t fi;
404 ctf_id_t type;
405
406 if ((type = ctf_func_info (state->cds_fp, i, &fi)) == CTF_ERR)
407 switch (ctf_errno (state->cds_fp))
408 {
409 /* Most errors are just an indication that this symbol is not a data
410 symbol, but this one indicates that we were called wrong, on a
411 CTF file with no associated symbol table. */
412 case ECTF_NOSYMTAB:
413 return -1;
414 case ECTF_NOTDATA:
415 case ECTF_NOTFUNC:
416 case ECTF_NOFUNCDAT:
417 continue;
418 }
419
420 /* Return type and all args. */
421 if ((bit = ctf_type_aname (state->cds_fp, type)) == NULL)
422 {
423 err = "look up return type";
424 goto err;
425 }
426
427 /* Replace in the returned string, dropping in the function name. */
428
429 sym_name = ctf_lookup_symbol_name (fp, i);
430 if (sym_name[0] != '\0')
431 {
432 char *retstar;
433 char *new_bit;
434 char *walk;
435
436 new_bit = malloc (strlen (bit) + 1 + strlen (sym_name));
437 if (!new_bit)
438 goto oom;
439
440 /* See ctf_type_aname. */
441 retstar = strstr (bit, "(*) (");
442 if (!ctf_assert (fp, retstar))
443 goto assert_err;
444 retstar += 2; /* After the '*' */
445
446 /* C is not good at search-and-replace. */
447 walk = new_bit;
448 memcpy (walk, bit, retstar - bit);
449 walk += (retstar - bit);
450 strcpy (walk, sym_name);
451 walk += strlen (sym_name);
452 strcpy (walk, retstar);
453
454 free (bit);
455 bit = new_bit;
456 }
457
458 if (asprintf (&str, "Symbol 0x%lx: %s", (unsigned long) i, bit) < 0)
459 goto oom;
460 free (bit);
461
462 ctf_dump_append (state, str);
463 continue;
464
465 err:
466 ctf_err_warn (fp, 1, "Cannot %s dumping function type for "
467 "symbol 0x%li: %s", err, (unsigned long) i,
468 ctf_errmsg (ctf_errno (state->cds_fp)));
469 free (bit);
470 return -1; /* errno is set for us. */
471
472 oom:
473 free (bit);
474 return (ctf_set_errno (fp, errno));
475
476 assert_err:
477 free (bit);
478 return -1; /* errno is set for us. */
479 }
480 return 0;
481 }
482
483 /* Dump a single variable into the cds_items. */
484 static int
485 ctf_dump_var (const char *name, ctf_id_t type, void *arg)
486 {
487 char *str;
488 char *typestr;
489 ctf_dump_state_t *state = arg;
490
491 if (asprintf (&str, "%s -> ", name) < 0)
492 return (ctf_set_errno (state->cds_fp, errno));
493
494 if ((typestr = ctf_dump_format_type (state->cds_fp, type,
495 CTF_ADD_ROOT)) == NULL)
496 {
497 free (str);
498 return -1; /* errno is set for us. */
499 }
500
501 str = str_append (str, typestr);
502 free (typestr);
503
504 ctf_dump_append (state, str);
505 return 0;
506 }
507
508 /* Dump a single member into the string in the membstate. */
509 static int
510 ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
511 int depth, void *arg)
512 {
513 ctf_dump_membstate_t *state = arg;
514 char *typestr = NULL;
515 char *bit = NULL;
516 ctf_encoding_t ep;
517 ssize_t i;
518
519 for (i = 0; i < depth; i++)
520 *state->cdm_str = str_append (*state->cdm_str, " ");
521
522 if ((typestr = ctf_type_aname (state->cdm_fp, id)) == NULL)
523 {
524 if (id == 0 || ctf_errno (state->cdm_fp) == ECTF_NONREPRESENTABLE)
525 {
526 if (asprintf (&bit, " [0x%lx] (type not represented in CTF)",
527 offset) < 0)
528 goto oom;
529
530 *state->cdm_str = str_append (*state->cdm_str, bit);
531 free (typestr);
532 free (bit);
533 return 0;
534 }
535
536 goto oom;
537 }
538
539 if (asprintf (&bit, " [0x%lx] (ID 0x%lx) (kind %i) %s %s (aligned at 0x%lx",
540 offset, id, ctf_type_kind (state->cdm_fp, id), typestr, name,
541 (unsigned long) ctf_type_align (state->cdm_fp, id)) < 0)
542 goto oom;
543 *state->cdm_str = str_append (*state->cdm_str, bit);
544 free (typestr);
545 free (bit);
546 typestr = NULL;
547 bit = NULL;
548
549 if ((ctf_type_kind (state->cdm_fp, id) == CTF_K_INTEGER)
550 || (ctf_type_kind (state->cdm_fp, id) == CTF_K_FLOAT)
551 || (ctf_is_slice (state->cdm_fp, id, &ep) == CTF_K_ENUM))
552 {
553 ctf_type_encoding (state->cdm_fp, id, &ep);
554 if (asprintf (&bit, ", format 0x%x, offset:bits 0x%x:0x%x", ep.cte_format,
555 ep.cte_offset, ep.cte_bits) < 0)
556 goto oom;
557 *state->cdm_str = str_append (*state->cdm_str, bit);
558 free (bit);
559 bit = NULL;
560 }
561
562 *state->cdm_str = str_append (*state->cdm_str, ")\n");
563 return 0;
564
565 oom:
566 free (typestr);
567 free (bit);
568 return (ctf_set_errno (state->cdm_fp, errno));
569 }
570
571 /* Dump a single type into the cds_items. */
572 static int
573 ctf_dump_type (ctf_id_t id, int flag, void *arg)
574 {
575 char *str;
576 const char *err;
577 ctf_dump_state_t *state = arg;
578 ctf_dump_membstate_t membstate = { &str, state->cds_fp };
579 size_t len;
580
581 if ((str = ctf_dump_format_type (state->cds_fp, id, flag)) == NULL)
582 {
583 err = "format type";
584 goto err;
585 }
586
587 str = str_append (str, "\n");
588 if ((ctf_type_visit (state->cds_fp, id, ctf_dump_member, &membstate)) < 0)
589 {
590 if (id == 0 || ctf_errno (state->cds_fp) == ECTF_NONREPRESENTABLE)
591 {
592 ctf_dump_append (state, str);
593 return 0;
594 }
595 err = "visit members";
596 goto err;
597 }
598
599 /* Trim off the last linefeed added by ctf_dump_member(). */
600 len = strlen (str);
601 if (str[len-1] == '\n')
602 str[len-1] = '\0';
603
604 ctf_dump_append (state, str);
605 return 0;
606
607 err:
608 ctf_dprintf ("Cannot %s dumping type 0x%lx: %s\n", err, id,
609 ctf_errmsg (ctf_errno (state->cds_fp)));
610 free (str);
611 return -1; /* errno is set for us. */
612 }
613
614 /* Dump the string table into the cds_items. */
615
616 static int
617 ctf_dump_str (ctf_file_t *fp, ctf_dump_state_t *state)
618 {
619 const char *s = fp->ctf_str[CTF_STRTAB_0].cts_strs;
620
621 for (; s < fp->ctf_str[CTF_STRTAB_0].cts_strs +
622 fp->ctf_str[CTF_STRTAB_0].cts_len;)
623 {
624 char *str;
625 if (asprintf (&str, "%lx: %s",
626 (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
627 s) < 0)
628 return (ctf_set_errno (fp, errno));
629 ctf_dump_append (state, str);
630 s += strlen (s) + 1;
631 }
632
633 return 0;
634 }
635
636 /* Dump a particular section of a CTF file, in textual form. Call with a
637 pointer to a NULL STATE: each call emits a dynamically allocated string
638 containing a description of one entity in the specified section, in order.
639 Only the first call (with a NULL state) may vary SECT. Once the CTF section
640 has been entirely dumped, the call returns NULL and frees and annuls the
641 STATE, ready for another section to be dumped. The returned textual content
642 may span multiple lines: between each call the FUNC is called with one
643 textual line at a time, and should return a suitably decorated line (it can
644 allocate a new one and return it if it likes). */
645
646 char *
647 ctf_dump (ctf_file_t *fp, ctf_dump_state_t **statep, ctf_sect_names_t sect,
648 ctf_dump_decorate_f *func, void *arg)
649 {
650 char *str;
651 char *line;
652 ctf_dump_state_t *state = NULL;
653
654 if (*statep == NULL)
655 {
656 /* Data collection. Transforming a call-at-a-time iterator into a
657 return-at-a-time iterator in a language without call/cc is annoying. It
658 is easiest to simply collect everything at once and then return it bit
659 by bit. The first call will take (much) longer than otherwise, but the
660 amortized time needed is the same. */
661
662 if ((*statep = malloc (sizeof (struct ctf_dump_state))) == NULL)
663 {
664 ctf_set_errno (fp, ENOMEM);
665 goto end;
666 }
667 state = *statep;
668
669 memset (state, 0, sizeof (struct ctf_dump_state));
670 state->cds_fp = fp;
671 state->cds_sect = sect;
672
673 switch (sect)
674 {
675 case CTF_SECT_HEADER:
676 ctf_dump_header (fp, state);
677 break;
678 case CTF_SECT_LABEL:
679 if (ctf_label_iter (fp, ctf_dump_label, state) < 0)
680 {
681 if (ctf_errno (fp) != ECTF_NOLABELDATA)
682 goto end; /* errno is set for us. */
683 ctf_set_errno (fp, 0);
684 }
685 break;
686 case CTF_SECT_OBJT:
687 if (ctf_dump_objts (fp, state) < 0)
688 goto end; /* errno is set for us. */
689 break;
690 case CTF_SECT_FUNC:
691 if (ctf_dump_funcs (fp, state) < 0)
692 goto end; /* errno is set for us. */
693 break;
694 case CTF_SECT_VAR:
695 if (ctf_variable_iter (fp, ctf_dump_var, state) < 0)
696 goto end; /* errno is set for us. */
697 break;
698 case CTF_SECT_TYPE:
699 if (ctf_type_iter_all (fp, ctf_dump_type, state) < 0)
700 goto end; /* errno is set for us. */
701 break;
702 case CTF_SECT_STR:
703 ctf_dump_str (fp, state);
704 break;
705 default:
706 ctf_set_errno (fp, ECTF_DUMPSECTUNKNOWN);
707 goto end;
708 }
709 }
710 else
711 {
712 state = *statep;
713
714 if (state->cds_sect != sect)
715 {
716 ctf_set_errno (fp, ECTF_DUMPSECTCHANGED);
717 goto end;
718 }
719 }
720
721 if (state->cds_current == NULL)
722 state->cds_current = ctf_list_next (&state->cds_items);
723 else
724 state->cds_current = ctf_list_next (state->cds_current);
725
726 if (state->cds_current == NULL)
727 goto end;
728
729 /* Hookery. There is some extra complexity to preserve linefeeds within each
730 item while removing linefeeds at the end. */
731 if (func)
732 {
733 size_t len;
734
735 str = NULL;
736 for (line = state->cds_current->cdi_item; line && *line; )
737 {
738 char *nline = line;
739 char *ret;
740
741 nline = strchr (line, '\n');
742 if (nline)
743 nline[0] = '\0';
744
745 ret = func (sect, line, arg);
746 str = str_append (str, ret);
747 str = str_append (str, "\n");
748 if (ret != line)
749 free (ret);
750
751 if (nline)
752 {
753 nline[0] = '\n';
754 nline++;
755 }
756
757 line = nline;
758 }
759
760 len = strlen (str);
761
762 if (str[len-1] == '\n')
763 str[len-1] = '\0';
764 }
765 else
766 {
767 str = strdup (state->cds_current->cdi_item);
768 if (!str)
769 {
770 ctf_set_errno (fp, ENOMEM);
771 return str;
772 }
773 }
774
775 ctf_set_errno (fp, 0);
776 return str;
777
778 end:
779 ctf_dump_free (state);
780 free (state);
781 ctf_set_errno (fp, 0);
782 *statep = NULL;
783 return NULL;
784 }
This page took 0.045349 seconds and 5 git commands to generate.