Prep 1.27
[dwarves.git] / ctracer.c
blobcb7e81fd5a81c8c86f901d4b13a1cb3db8c83cd0
1 /*
2 SPDX-License-Identifier: GPL-2.0-only
4 Copyright (C) 2006 Mandriva Conectiva S.A.
5 Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
6 */
8 #include <argp.h>
9 #include <elf.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <gelf.h>
13 #include <limits.h>
14 #include <search.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
22 #include "dwarves_reorganize.h"
23 #include "dwarves_emit.h"
24 #include "dwarves.h"
25 #include "dutil.h"
26 #include "elf_symtab.h"
29 * target class name
31 static char *class_name;
34 * List of compilation units being looked for functions with
35 * pointers to the specified struct.
37 static struct cus *methods_cus;
39 /**
40 * Mini class, the subset of the traced class that is collected at the probes
43 static struct class *mini_class;
46 * Directory where to generate source files
48 static const char *src_dir = ".";
51 * Where to print the ctracer_methods.stp file
53 static FILE *fp_methods;
56 * Where to print the ctracer_collector.c file
58 static FILE *fp_collector;
61 * Where to print the ctracer_classes.h file
63 static FILE *fp_classes;
66 * blacklist __init marked functions, i.e. functions that are
67 * in the ".init.text" ELF section and are thus discarded after
68 * boot.
70 static struct strlist *init_blacklist;
73 * List of definitions and forward declarations already emitted for
74 * methods_cus, to avoid duplication.
76 static struct type_emissions emissions;
79 * CU blacklist: if a "blacklist.cu" file is present, don't consider the
80 * CUs listed. Use a default of blacklist.cu.
82 static const char *cu_blacklist_filename = "blacklist.cu";
84 static struct strlist *cu_blacklist;
86 static struct cu *cu_filter(struct cu *cu)
88 if (strlist__has_entry(cu_blacklist, cu->name))
89 return NULL;
90 return cu;
94 * List of probes and kretprobes already emitted, this is a hack to cope with
95 * name space collisions, a better solution would be to in these cases to use the
96 * compilation unit name (net/ipv4/tcp.o, for instance) as a prefix when a
97 * static function has the same name in multiple compilation units (aka object
98 * files).
100 static void *probes_emitted;
102 struct structure {
103 struct list_head node;
104 struct tag *class;
105 struct cu *cu;
108 static struct structure *structure__new(struct tag *class, struct cu *cu)
110 struct structure *st = malloc(sizeof(*st));
112 if (st != NULL) {
113 st->class = class;
114 st->cu = cu;
117 return st;
121 * structs that can be casted to the target class, e.g. i.e. that has the target
122 * class at its first member.
124 static LIST_HEAD(aliases);
127 * structs have pointers to the target class.
129 static LIST_HEAD(pointers);
131 static const char *structure__name(const struct structure *st)
133 return class__name(tag__class(st->class));
136 static struct structure *structures__find(struct list_head *list, const char *name)
138 struct structure *pos;
140 if (name == NULL)
141 return NULL;
143 list_for_each_entry(pos, list, node)
144 if (strcmp(structure__name(pos), name) == 0)
145 return pos;
147 return NULL;
150 static void structures__add(struct list_head *list, struct tag *class, struct cu *cu)
152 struct structure *str = structure__new(class, cu);
154 if (str != NULL)
155 list_add(&str->node, list);
158 static int methods__compare(const void *a, const void *b)
160 return strcmp(a, b);
163 static int methods__add(void **table, const char *str)
165 char **s = tsearch(str, table, methods__compare);
167 if (s != NULL) {
168 if (*s == str) {
169 char *dup = strdup(str);
170 if (dup != NULL)
171 *s = dup;
172 else {
173 tdelete(str, table, methods__compare);
174 return -1;
176 } else
177 return -1;
178 } else
179 return -1;
181 return 0;
184 static void method__add(struct cu *cu, struct function *function, uint32_t id)
186 list_add(&function->tool_node, &cu->tool_list);
187 function->priv = (void *)(long)id;
191 * We want just the function tags that have as one of its parameters
192 * a pointer to the specified "class" (a struct, unions can be added later).
194 static struct function *function__filter(struct function *function,
195 struct cu *cu, type_id_t target_type_id)
197 if (function__inlined(function) ||
198 function->abstract_origin != 0 ||
199 !list_empty(&function->tool_node) ||
200 !ftype__has_parm_of_type(&function->proto, target_type_id, cu) ||
201 strlist__has_entry(init_blacklist, function__name(function))) {
202 return NULL;
205 return function;
209 * Iterate thru all the tags in the compilation unit, looking just for the
210 * function tags that have as one of its parameters a pointer to
211 * the specified "class" (struct).
213 static int cu_find_methods_iterator(struct cu *cu, void *cookie)
215 type_id_t target_type_id;
216 uint32_t function_id;
217 struct function *function;
218 struct tag *target = cu__find_struct_by_name(cu, cookie, 0,
219 &target_type_id);
221 INIT_LIST_HEAD(&cu->tool_list);
223 if (target == NULL)
224 return 0;
226 cu__for_each_function(cu, function_id, function)
227 if (function__filter(function, cu, target_type_id))
228 method__add(cu, function, function_id);
230 return 0;
233 static struct class_member *class_member__bitfield_tail(struct class_member *head,
234 struct class *class)
236 struct class_member *tail = head,
237 *member = list_prepare_entry(head,
238 class__tags(class),
239 tag.node);
240 list_for_each_entry_continue(member, class__tags(class), tag.node)
241 if (member->byte_offset == head->byte_offset)
242 tail = member;
243 else
244 break;
246 return tail;
250 * Bitfields are removed as one for simplification right now.
252 static struct class_member *class__remove_member(struct class *class, const struct cu *cu,
253 struct class_member *member)
255 size_t size = member->byte_size;
256 struct class_member *bitfield_tail = NULL;
257 struct list_head *next;
258 uint16_t member_hole = member->hole;
260 if (member->bitfield_size != 0) {
261 bitfield_tail = class_member__bitfield_tail(member, class);
262 member_hole = bitfield_tail->hole;
265 * Is this the first member?
267 if (member->tag.node.prev == class__tags(class)) {
268 class->type.size -= size + member_hole;
269 class__subtract_offsets_from(class, bitfield_tail ?: member,
270 size + member_hole);
272 * Is this the last member?
274 } else if (member->tag.node.next == class__tags(class)) {
275 if (size + class->padding >= cu->addr_size) {
276 class->type.size -= size + class->padding;
277 class->padding = 0;
278 } else
279 class->padding += size;
280 } else {
281 if (size + member_hole >= cu->addr_size) {
282 class->type.size -= size + member_hole;
283 class__subtract_offsets_from(class,
284 bitfield_tail ?: member,
285 size + member_hole);
286 } else {
287 struct class_member *from_prev =
288 list_entry(member->tag.node.prev,
289 struct class_member,
290 tag.node);
291 if (from_prev->hole == 0)
292 class->nr_holes++;
293 from_prev->hole += size + member_hole;
296 if (member_hole != 0)
297 class->nr_holes--;
299 if (bitfield_tail != NULL) {
300 next = bitfield_tail->tag.node.next;
301 list_del_range(&member->tag.node, &bitfield_tail->tag.node);
302 if (bitfield_tail->bit_hole != 0)
303 class->nr_bit_holes--;
304 } else {
305 next = member->tag.node.next;
306 list_del(&member->tag.node);
309 return list_entry(next, struct class_member, tag.node);
312 static size_t class__find_biggest_member_name(const struct class *class)
314 struct class_member *pos;
315 size_t biggest_name_len = 0;
317 type__for_each_data_member(&class->type, pos) {
318 const size_t len = pos->name ?
319 strlen(class_member__name(pos)) : 0;
321 if (len > biggest_name_len)
322 biggest_name_len = len;
325 return biggest_name_len;
328 static void class__emit_class_state_collector(struct class *class, struct class *clone)
330 struct class_member *pos;
331 int len = class__find_biggest_member_name(clone);
333 fprintf(fp_collector,
334 "void ctracer__class_state(const void *from, void *to)\n"
335 "{\n"
336 "\tconst struct %s *obj = from;\n"
337 "\tstruct %s *mini_obj = to;\n\n",
338 class__name(class), class__name(clone));
339 type__for_each_data_member(&clone->type, pos)
340 fprintf(fp_collector, "\tmini_obj->%-*s = obj->%s;\n", len,
341 class_member__name(pos),
342 class_member__name(pos));
343 fputs("}\n\n", fp_collector);
346 static struct class *class__clone_base_types(const struct tag *tag,
347 struct cu *cu,
348 const char *new_class_name)
350 struct class *class = tag__class(tag);
351 struct class_member *pos, *next;
352 struct class *clone = class__clone(class, new_class_name);
354 if (clone == NULL)
355 return NULL;
357 type__for_each_data_member_safe(&clone->type, pos, next) {
358 struct tag *member_type = cu__type(cu, pos->tag.type);
360 tag__assert_search_result(member_type, pos->tag.tag, class_member__name(pos));
361 if (!tag__is_base_type(member_type, cu)) {
362 next = class__remove_member(clone, cu, pos);
363 class_member__delete(pos);
366 class__fixup_alignment(clone, cu);
367 class__reorganize(clone, cu, 0, NULL);
368 return clone;
372 * Converter to the legacy ostra tables, will be much improved in the future.
374 static void emit_struct_member_table_entry(FILE *fp,
375 int field, const char *name,
376 int traced, const char *hooks)
378 fprintf(fp, "%u:%s:", field, name);
379 if (traced)
380 fprintf(fp, "yes:%%object->%s:u:%s:none\n", name, hooks);
381 else
382 fprintf(fp, "no:None:None:%s:dev_null\n", hooks);
386 * Generates a converter to the ostra lebacy tables format, needef by
387 * ostra-cg to preprocess the raw data collected from the debugfs/relay
388 * channel.
390 static int class__emit_ostra_converter(struct tag *tag)
392 struct class *class = tag__class(tag);
393 struct class_member *pos;
394 struct type *type = &mini_class->type;
395 int field = 0, first = 1;
396 char filename[128];
397 char parm_list[1024] = "";
398 char *p = parm_list;
399 size_t n;
400 size_t plen = sizeof(parm_list);
401 FILE *fp_fields, *fp_converter;
402 const char *name = class__name(class);
404 snprintf(filename, sizeof(filename), "%s/%s.fields", src_dir, name);
405 fp_fields = fopen(filename, "w");
406 if (fp_fields == NULL) {
407 fprintf(stderr, "ctracer: couldn't create %s\n", filename);
408 exit(EXIT_FAILURE);
411 snprintf(filename, sizeof(filename), "%s/ctracer2ostra.c", src_dir);
413 fp_converter = fopen(filename, "w");
414 if (fp_converter == NULL) {
415 fprintf(stderr, "ctracer: couldn't create %s\n", filename);
416 exit(EXIT_FAILURE);
419 fputs("#include \"ctracer_classes.h\"\n"
420 "#include <stdio.h>\n"
421 "#include <string.h>\n"
422 "#include \"ctracer_relay.h\"\n\n", fp_converter);
423 emit_struct_member_table_entry(fp_fields, field++, "action", 0,
424 "entry,exit");
425 emit_struct_member_table_entry(fp_fields, field++, "function_id", 0,
426 "entry,exit");
427 emit_struct_member_table_entry(fp_fields, field++, "object", 1,
428 "entry,exit");
430 fprintf(fp_converter, "\n"
431 "int main(void)\n"
432 "{\n"
433 "\twhile (1) {\n"
434 "\t\tstruct trace_entry hdr;\n"
435 "\t\tstruct ctracer__mini_%s obj;\n"
436 "\n"
437 "\t\tif (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))\n"
438 "\t\t\tbreak;\n"
439 "\n"
440 "\t\tfprintf(stdout, \"%%llu %%c:%%llu:%%p\",\n"
441 "\t\t\thdr.nsec,\n"
442 "\t\t\thdr.probe_type ? 'o' : 'i',\n"
443 "\t\t\thdr.function_id,\n"
444 "\t\t\thdr.object);\n"
445 "\n"
446 "\t\tif (read(0, &obj, sizeof(obj)) != sizeof(obj))\n"
447 "\t\t\tbreak;\n"
448 "\t\tfprintf(stdout,\n"
449 "\t\t\t\":", name);
451 type__for_each_data_member(type, pos) {
452 if (first)
453 first = 0;
454 else {
455 fputc(':', fp_converter);
456 n = snprintf(p, plen, ",\n\t\t\t ");
457 plen -= n; p += n;
459 fprintf(fp_converter, "%%u");
460 n = snprintf(p, plen, "obj.%s", class_member__name(pos));
461 plen -= n; p += n;
462 emit_struct_member_table_entry(fp_fields, field++,
463 class_member__name(pos),
464 1, "entry,exit");
466 fprintf(fp_converter,
467 "\\n\",\n\t\t\t %s);\n"
468 "\t}\n"
469 "\treturn 0;\n"
470 "}\n", parm_list);
471 fclose(fp_fields);
472 fclose(fp_converter);
473 return 0;
477 * We want just the DW_TAG_structure_type tags that have a member that is a pointer
478 * to the target class.
480 static struct tag *pointer_filter(struct tag *tag, struct cu *cu,
481 type_id_t target_type_id)
483 struct type *type;
484 struct class_member *pos;
485 const char *class_name;
487 if (!tag__is_struct(tag))
488 return NULL;
490 type = tag__type(tag);
491 if (type->nr_members == 0)
492 return NULL;
494 class_name = class__name(tag__class(tag));
495 if (class_name == NULL || structures__find(&pointers, class_name))
496 return NULL;
498 type__for_each_member(type, pos) {
499 struct tag *ctype = cu__type(cu, pos->tag.type);
501 tag__assert_search_result(ctype, pos->tag.tag, class_member__name(pos));
502 if (tag__is_pointer_to(ctype, target_type_id))
503 return tag;
506 return NULL;
510 * Iterate thru all the tags in the compilation unit, looking for classes
511 * that have as one member that is a pointer to the target type.
513 static int cu_find_pointers_iterator(struct cu *cu, void *class_name)
515 type_id_t target_type_id, id;
516 struct tag *target = cu__find_struct_by_name(cu, class_name, 0,
517 &target_type_id), *pos;
519 if (target == NULL)
520 return 0;
522 cu__for_each_type(cu, id, pos)
523 if (pointer_filter(pos, cu, target_type_id))
524 structures__add(&pointers, pos, cu);
526 return 0;
529 static void class__find_pointers(const char *class_name)
531 cus__for_each_cu(methods_cus, cu_find_pointers_iterator, (void *)class_name, cu_filter);
535 * We want just the DW_TAG_structure_type tags that have as its first member
536 * a struct of type target.
538 static struct tag *alias_filter(struct tag *tag, type_id_t target_type_id)
540 struct type *type;
541 struct class_member *first_member;
543 if (!tag__is_struct(tag))
544 return NULL;
546 type = tag__type(tag);
547 if (type->nr_members == 0)
548 return NULL;
550 first_member = list_first_entry(&type->namespace.tags,
551 struct class_member, tag.node);
552 if (first_member->tag.type != target_type_id)
553 return NULL;
555 if (structures__find(&aliases, class__name(tag__class(tag))))
556 return NULL;
558 return tag;
561 static void class__find_aliases(const char *class_name);
564 * Iterate thru all the tags in the compilation unit, looking for classes
565 * that have as its first member the specified "class" (struct).
567 static int cu_find_aliases_iterator(struct cu *cu, void *class_name)
569 type_id_t target_type_id, id;
570 struct tag *target = cu__find_struct_by_name(cu, class_name, 0,
571 &target_type_id), *pos;
572 if (target == NULL)
573 return 0;
575 cu__for_each_type(cu, id, pos) {
576 if (alias_filter(pos, target_type_id)) {
577 const char *alias_name = class__name(tag__class(pos));
579 structures__add(&aliases, pos, cu);
582 * Now find aliases to this alias, e.g.:
584 * struct tcp_sock {
585 * struct inet_connection_sock {
586 * struct inet_sock {
587 * struct sock {
593 class__find_aliases(alias_name);
597 return 0;
600 static void class__find_aliases(const char *class_name)
602 cus__for_each_cu(methods_cus, cu_find_aliases_iterator, (void *)class_name, cu_filter);
605 static void emit_list_of_types(struct list_head *list)
607 struct structure *pos;
609 list_for_each_entry(pos, list, node) {
610 struct type *type = tag__type(pos->class);
612 * Lets look at the other CUs, perhaps we have already
613 * emmited this one
615 if (type_emissions__find_definition(&emissions, type__tag(type)->tag, structure__name(pos))) {
616 type->definition_emitted = 1;
617 continue;
619 type__emit_definitions(pos->class, pos->cu, &emissions,
620 fp_classes);
621 type->definition_emitted = 1;
622 type__emit(pos->class, pos->cu, NULL, NULL, fp_classes);
623 tag__type(pos->class)->definition_emitted = 1;
624 fputc('\n', fp_classes);
628 static int class__emit_classes(struct tag *tag, struct cu *cu)
630 struct class *class = tag__class(tag);
631 int err = -1;
632 char mini_class_name[128];
634 snprintf(mini_class_name, sizeof(mini_class_name), "ctracer__mini_%s",
635 class__name(class));
637 mini_class = class__clone_base_types(tag, cu, mini_class_name);
638 if (mini_class == NULL)
639 goto out;
641 type__emit_definitions(tag, cu, &emissions, fp_classes);
643 type__emit(tag, cu, NULL, NULL, fp_classes);
644 fputs("\n/* class aliases */\n\n", fp_classes);
646 emit_list_of_types(&aliases);
648 fputs("\n/* class with pointers */\n\n", fp_classes);
650 emit_list_of_types(&pointers);
652 class__fprintf(mini_class, cu, fp_classes);
653 fputs(";\n\n", fp_classes);
654 class__emit_class_state_collector(class, mini_class);
655 err = 0;
656 out:
657 return err;
661 * Emit the kprobes routine for one of the selected "methods", later we'll
662 * put this into the 'kprobes' table, in cu_emit_kprobes_table_iterator.
664 * This marks the function entry, function__emit_kretprobes will emit the
665 * probe for the function exit.
667 static int function__emit_probes(struct function *func, uint32_t function_id,
668 const struct cu *cu,
669 const type_id_t target_type_id, int probe_type,
670 const char *member)
672 struct parameter *pos;
673 const char *name = function__name(func);
675 fprintf(fp_methods, "probe %s%s = kernel.function(\"%s@%s\")%s\n"
676 "{\n"
677 "}\n\n"
678 "probe %s%s\n"
679 "{\n", name,
680 probe_type == 0 ? "" : "__return",
681 name,
682 cu->name,
683 probe_type == 0 ? "" : ".return",
684 name,
685 probe_type == 0 ? "" : "__return");
687 list_for_each_entry(pos, &func->proto.parms, tag.node) {
688 struct tag *type = cu__type(cu, pos->tag.type);
690 tag__assert_search_result(type, pos->tag.tag, parameter__name(pos));
691 if (!tag__is_pointer_to(type, target_type_id))
692 continue;
694 if (member != NULL)
695 fprintf(fp_methods, "\tif ($%s)\n\t", parameter__name(pos));
697 fprintf(fp_methods,
698 "\tctracer__method_hook(%d, %d, $%s%s%s, %d);\n",
699 probe_type,
700 function_id,
701 parameter__name(pos),
702 member ? "->" : "", member ?: "",
703 class__size(mini_class));
704 break;
707 fputs("}\n\n", fp_methods);
708 fflush(fp_methods);
710 return 0;
714 * Iterate thru the list of methods previously collected by
715 * cu_find_methods_iterator, emitting the probes for function entry.
717 static int cu_emit_probes_iterator(struct cu *cu, void *cookie)
719 type_id_t target_type_id;
720 struct tag *target = cu__find_struct_by_name(cu, cookie, 0, &target_type_id);
721 struct function *pos;
723 /* OK, this type is not present in this compile unit */
724 if (target == NULL)
725 return 0;
727 list_for_each_entry(pos, &cu->tool_list, tool_node) {
728 uint32_t function_id = (long)pos->priv;
730 if (methods__add(&probes_emitted, function__name(pos)) != 0)
731 continue;
732 function__emit_probes(pos, function_id, cu, target_type_id, 0, NULL); /* entry */
733 function__emit_probes(pos, function_id, cu, target_type_id, 1, NULL); /* exit */
736 return 0;
740 * Iterate thru the list of methods previously collected by
741 * cu_find_methods_iterator, emitting the probes for function entry.
743 static int cu_emit_pointer_probes_iterator(struct cu *cu, void *cookie)
745 type_id_t target_type_id, pointer_id;
746 struct tag *target, *pointer;
747 struct function *pos_tag;
748 struct class_member *pos_member;
750 /* This CU doesn't have our classes */
751 if (list_empty(&cu->tool_list))
752 return 0;
754 target = cu__find_struct_by_name(cu, class_name, 1, &target_type_id);
755 pointer = cu__find_struct_by_name(cu, cookie, 0, &pointer_id);
757 /* OK, this type is not present in this compile unit */
758 if (target == NULL || pointer == NULL)
759 return 0;
761 /* for now just for the first member that is a pointer */
762 type__for_each_member(tag__type(pointer), pos_member) {
763 struct tag *ctype = cu__type(cu, pos_member->tag.type);
765 tag__assert_search_result(ctype, pos_member->tag.tag, class_member__name(pos_member));
766 if (tag__is_pointer_to(ctype, target_type_id))
767 break;
770 list_for_each_entry(pos_tag, &cu->tool_list, tool_node) {
771 uint32_t function_id = (long)pos_tag->priv;
773 if (methods__add(&probes_emitted, function__name(pos_tag)) != 0)
774 continue;
776 function__emit_probes(pos_tag, function_id, cu, target_type_id, 0,
777 class_member__name(pos_member)); /* entry */
778 function__emit_probes(pos_tag, function_id, cu, target_type_id, 1,
779 class_member__name(pos_member)); /* exit */
782 return 0;
786 * Iterate thru the list of methods previously collected by
787 * cu_find_methods_iterator, creating the functions table that will
788 * be used by ostra-cg
790 static int cu_emit_functions_table(struct cu *cu, void *fp)
792 struct function *pos;
794 list_for_each_entry(pos, &cu->tool_list, tool_node)
795 if (pos->priv != NULL) {
796 uint32_t function_id = (long)pos->priv;
797 fprintf(fp, "%d:%s\n", function_id, function__name(pos));
798 pos->priv = NULL;
801 return 0;
804 static int elf__open(const char *filename)
806 int fd = open(filename, O_RDONLY);
808 if (fd < 0)
809 return -1;
811 int err = -1;
813 if (elf_version(EV_CURRENT) == EV_NONE) {
814 fprintf(stderr, "%s: cannot set libelf version.\n", __func__);
815 goto out_close;
818 Elf *elf = elf_begin(fd, ELF_C_READ_MMAP, NULL);
819 if (elf == NULL) {
820 fprintf(stderr, "%s: cannot read %s ELF file.\n",
821 __func__, filename);
822 goto out_close;
825 GElf_Shdr shdr;
826 size_t init_index;
827 Elf_Scn *init = elf_section_by_name(elf, &shdr, ".init.text", &init_index);
828 if (init == NULL)
829 goto out_elf_end;
831 struct elf_symtab *symtab = elf_symtab__new(".symtab", elf);
832 if (symtab == NULL)
833 goto out_elf_end;
835 init_blacklist = strlist__new(true);
836 if (init_blacklist == NULL)
837 goto out_elf_symtab_delete;
839 uint32_t index;
840 GElf_Sym sym;
841 elf_symtab__for_each_symbol(symtab, index, sym) {
842 if (!elf_sym__is_local_function(&sym))
843 continue;
844 if (elf_sym__section(&sym) != init_index)
845 continue;
846 err = strlist__add(init_blacklist, elf_sym__name(&sym, symtab));
847 if (err == -ENOMEM) {
848 fprintf(stderr, "failed for %s(%d,%zd)\n", elf_sym__name(&sym, symtab),elf_sym__section(&sym),init_index);
849 goto out_delete_blacklist;
853 err = 0;
854 out_elf_symtab_delete:
855 elf_symtab__delete(symtab);
856 out_elf_end:
857 elf_end(elf);
858 out_close:
859 close(fd);
860 return err;
861 out_delete_blacklist:
862 strlist__delete(init_blacklist);
863 goto out_elf_symtab_delete;
866 /* Name and version of program. */
867 ARGP_PROGRAM_VERSION_HOOK_DEF = dwarves_print_version;
869 static const struct argp_option ctracer__options[] = {
871 .key = 'd',
872 .name = "src_dir",
873 .arg = "SRC_DIR",
874 .doc = "generate source files in this directory",
877 .key = 'C',
878 .name = "cu_blacklist",
879 .arg = "FILE",
880 .doc = "Blacklist the CUs in FILE",
883 .key = 'D',
884 .name = "dir",
885 .arg = "DIR",
886 .doc = "load files in this directory",
889 .key = 'g',
890 .name = "glob",
891 .arg = "GLOB",
892 .doc = "file mask to load",
895 .key = 'r',
896 .name = "recursive",
897 .doc = "recursively load files",
900 .name = NULL,
904 static const char *dirname, *glob;
905 static int recursive;
907 static error_t ctracer__options_parser(int key, char *arg,
908 struct argp_state *state __maybe_unused)
910 switch (key) {
911 case 'd': src_dir = arg; break;
912 case 'C': cu_blacklist_filename = arg; break;
913 case 'D': dirname = arg; break;
914 case 'g': glob = arg; break;
915 case 'r': recursive = 1; break;
916 default: return ARGP_ERR_UNKNOWN;
918 return 0;
921 static const char ctracer__args_doc[] = "FILE CLASS";
923 static struct argp ctracer__argp = {
924 .options = ctracer__options,
925 .parser = ctracer__options_parser,
926 .args_doc = ctracer__args_doc,
929 int main(int argc, char *argv[])
931 int remaining, err;
932 struct tag *class;
933 struct cu *cu;
934 char *filename;
935 char functions_filename[PATH_MAX];
936 char methods_filename[PATH_MAX];
937 char collector_filename[PATH_MAX];
938 char classes_filename[PATH_MAX];
939 struct structure *pos;
940 FILE *fp_functions;
941 int rc = EXIT_FAILURE;
943 if (dwarves__init()) {
944 fputs("ctracer: insufficient memory\n", stderr);
945 goto out;
947 dwarves__resolve_cacheline_size(NULL, 0);
949 if (argp_parse(&ctracer__argp, argc, argv, 0, &remaining, NULL) ||
950 remaining < argc) {
951 switch (argc - remaining) {
952 case 1: goto failure;
953 case 2: filename = argv[remaining++];
954 class_name = argv[remaining++]; break;
955 default: goto failure;
957 } else {
958 failure:
959 argp_help(&ctracer__argp, stderr, ARGP_HELP_SEE, argv[0]);
960 goto out;
963 type_emissions__init(&emissions, NULL);
966 * Create the methods_cus (Compilation Units) object where we will
967 * load the objects where we'll look for functions pointers to the
968 * specified class, i.e. to find its "methods", where we'll insert
969 * the entry and exit hooks.
971 methods_cus = cus__new();
972 if (methods_cus == NULL) {
973 fputs("ctracer: insufficient memory\n", stderr);
974 goto out;
978 * if --dir/-D was specified, recursively traverse the path looking for
979 * object files (compilation units) that match the glob specified (*.ko)
980 * for kernel modules, but could be "*.o" in the future when we support
981 * uprobes for user space tracing.
983 if (dirname != NULL && cus__load_dir(methods_cus, NULL, dirname, glob,
984 recursive) != 0) {
985 fprintf(stderr, "ctracer: couldn't load DWARF info "
986 "from %s dir with glob %s\n",
987 dirname, glob);
988 goto out;
992 * If a filename was specified, for instance "vmlinux", load it too.
994 if (filename != NULL) {
995 if (elf__open(filename)) {
996 fprintf(stderr, "ctracer: couldn't load ELF symtab "
997 "info from %s\n", filename);
998 goto out;
1000 err = cus__load_file(methods_cus, NULL, filename);
1001 if (err != 0) {
1002 cus__print_error_msg("ctracer", methods_cus, filename, err);
1003 goto out;
1008 * See if the specified struct exists:
1010 class = cus__find_struct_by_name(methods_cus, &cu, class_name, 0, NULL);
1011 if (class == NULL) {
1012 fprintf(stderr, "ctracer: struct %s not found!\n", class_name);
1013 goto out;
1016 snprintf(functions_filename, sizeof(functions_filename),
1017 "%s/%s.functions", src_dir, class__name(tag__class(class)));
1018 fp_functions = fopen(functions_filename, "w");
1019 if (fp_functions == NULL) {
1020 fprintf(stderr, "ctracer: couldn't create %s\n",
1021 functions_filename);
1022 goto out;
1025 snprintf(methods_filename, sizeof(methods_filename),
1026 "%s/ctracer_methods.stp", src_dir);
1027 fp_methods = fopen(methods_filename, "w");
1028 if (fp_methods == NULL) {
1029 fprintf(stderr, "ctracer: couldn't create %s\n",
1030 methods_filename);
1031 goto out;
1034 snprintf(collector_filename, sizeof(collector_filename),
1035 "%s/ctracer_collector.c", src_dir);
1036 fp_collector = fopen(collector_filename, "w");
1037 if (fp_collector == NULL) {
1038 fprintf(stderr, "ctracer: couldn't create %s\n",
1039 collector_filename);
1040 goto out;
1043 snprintf(classes_filename, sizeof(classes_filename),
1044 "%s/ctracer_classes.h", src_dir);
1045 fp_classes = fopen(classes_filename, "w");
1046 if (fp_classes == NULL) {
1047 fprintf(stderr, "ctracer: couldn't create %s\n",
1048 classes_filename);
1049 goto out;
1052 fputs("%{\n"
1053 "#include </home/acme/git/pahole/lib/ctracer_relay.h>\n"
1054 "%}\n"
1055 "function ctracer__method_hook(probe_type, func, object, state_len)\n"
1056 "%{\n"
1057 "\tctracer__method_hook(_stp_gettimeofday_ns(), "
1058 "THIS->probe_type, THIS->func, "
1059 "(void *)(long)THIS->object, "
1060 "THIS->state_len);\n"
1061 "%}\n\n", fp_methods);
1063 fputs("\n#include \"ctracer_classes.h\"\n\n", fp_collector);
1064 class__find_aliases(class_name);
1065 class__find_pointers(class_name);
1067 class__emit_classes(class, cu);
1068 fputc('\n', fp_collector);
1070 class__emit_ostra_converter(class);
1072 cu_blacklist = strlist__new(true);
1073 if (cu_blacklist != NULL)
1074 strlist__load(cu_blacklist, cu_blacklist_filename);
1076 cus__for_each_cu(methods_cus, cu_find_methods_iterator,
1077 class_name, cu_filter);
1078 cus__for_each_cu(methods_cus, cu_emit_probes_iterator,
1079 class_name, cu_filter);
1080 cus__for_each_cu(methods_cus, cu_emit_functions_table,
1081 fp_functions, cu_filter);
1083 list_for_each_entry(pos, &aliases, node) {
1084 const char *alias_name = structure__name(pos);
1086 cus__for_each_cu(methods_cus, cu_find_methods_iterator,
1087 (void *)alias_name, cu_filter);
1088 cus__for_each_cu(methods_cus, cu_emit_probes_iterator,
1089 (void *)alias_name, cu_filter);
1090 cus__for_each_cu(methods_cus, cu_emit_functions_table,
1091 fp_functions, cu_filter);
1094 list_for_each_entry(pos, &pointers, node) {
1095 const char *pointer_name = structure__name(pos);
1096 cus__for_each_cu(methods_cus, cu_find_methods_iterator,
1097 (void *)pointer_name, cu_filter);
1098 cus__for_each_cu(methods_cus, cu_emit_pointer_probes_iterator,
1099 (void *)pointer_name, cu_filter);
1100 cus__for_each_cu(methods_cus, cu_emit_functions_table, fp_functions,
1101 cu_filter);
1104 fclose(fp_methods);
1105 fclose(fp_collector);
1106 fclose(fp_functions);
1107 fclose(fp_classes);
1108 strlist__delete(cu_blacklist);
1110 rc = EXIT_SUCCESS;
1111 out:
1112 cus__delete(methods_cus);
1113 dwarves__exit();
1114 return rc;