12a6b7db3Sskrll /* call_graph.c - Create call graphs.
22a6b7db3Sskrll
3*cb63e24eSchristos Copyright (C) 1999-2024 Free Software Foundation, Inc.
42a6b7db3Sskrll
52a6b7db3Sskrll This file is part of GNU Binutils.
62a6b7db3Sskrll
72a6b7db3Sskrll This program is free software; you can redistribute it and/or modify
82a6b7db3Sskrll it under the terms of the GNU General Public License as published by
92a6b7db3Sskrll the Free Software Foundation; either version 3 of the License, or
102a6b7db3Sskrll (at your option) any later version.
112a6b7db3Sskrll
122a6b7db3Sskrll This program is distributed in the hope that it will be useful,
132a6b7db3Sskrll but WITHOUT ANY WARRANTY; without even the implied warranty of
142a6b7db3Sskrll MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
152a6b7db3Sskrll GNU General Public License for more details.
162a6b7db3Sskrll
172a6b7db3Sskrll You should have received a copy of the GNU General Public License
182a6b7db3Sskrll along with this program; if not, write to the Free Software
192a6b7db3Sskrll Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
202a6b7db3Sskrll 02110-1301, USA. */
212a6b7db3Sskrll
222a6b7db3Sskrll #include "gprof.h"
232a6b7db3Sskrll #include "search_list.h"
242a6b7db3Sskrll #include "source.h"
252a6b7db3Sskrll #include "symtab.h"
262a6b7db3Sskrll #include "cg_arcs.h"
272a6b7db3Sskrll #include "call_graph.h"
282a6b7db3Sskrll #include "corefile.h"
292a6b7db3Sskrll #include "gmon_io.h"
302a6b7db3Sskrll #include "gmon_out.h"
312a6b7db3Sskrll #include "sym_ids.h"
322a6b7db3Sskrll
332a6b7db3Sskrll void
cg_tally(bfd_vma from_pc,bfd_vma self_pc,unsigned long count)342a6b7db3Sskrll cg_tally (bfd_vma from_pc, bfd_vma self_pc, unsigned long count)
352a6b7db3Sskrll {
362a6b7db3Sskrll Sym *parent;
372a6b7db3Sskrll Sym *child;
382a6b7db3Sskrll
392a6b7db3Sskrll parent = sym_lookup (&symtab, from_pc);
402a6b7db3Sskrll child = sym_lookup (&symtab, self_pc);
412a6b7db3Sskrll
422a6b7db3Sskrll if (child == NULL || parent == NULL)
432a6b7db3Sskrll return;
442a6b7db3Sskrll
452a6b7db3Sskrll /* If we're doing line-by-line profiling, both the parent and the
462a6b7db3Sskrll child will probably point to line symbols instead of function
472a6b7db3Sskrll symbols. For the parent this is fine, since this identifies the
482a6b7db3Sskrll line number in the calling routing, but the child should always
492a6b7db3Sskrll point to a function entry point, so we back up in the symbol
502a6b7db3Sskrll table until we find it.
512a6b7db3Sskrll
522a6b7db3Sskrll For normal profiling, is_func will be set on all symbols, so this
532a6b7db3Sskrll code will do nothing. */
542a6b7db3Sskrll while (child >= symtab.base && ! child->is_func)
552a6b7db3Sskrll --child;
562a6b7db3Sskrll
572a6b7db3Sskrll if (child < symtab.base)
582a6b7db3Sskrll return;
592a6b7db3Sskrll
602a6b7db3Sskrll /* Keep arc if it is on INCL_ARCS table or if the INCL_ARCS table
612a6b7db3Sskrll is empty and it is not in the EXCL_ARCS table. */
622a6b7db3Sskrll if (sym_id_arc_is_present (&syms[INCL_ARCS], parent, child)
632a6b7db3Sskrll || (syms[INCL_ARCS].len == 0
642a6b7db3Sskrll && !sym_id_arc_is_present (&syms[EXCL_ARCS], parent, child)))
652a6b7db3Sskrll {
662a6b7db3Sskrll child->ncalls += count;
672a6b7db3Sskrll DBG (TALLYDEBUG,
682a6b7db3Sskrll printf (_("[cg_tally] arc from %s to %s traversed %lu times\n"),
692a6b7db3Sskrll parent->name, child->name, count));
702a6b7db3Sskrll arc_add (parent, child, count);
712a6b7db3Sskrll }
722a6b7db3Sskrll }
732a6b7db3Sskrll
742a6b7db3Sskrll /* Read a record from file IFP describing an arc in the function
752a6b7db3Sskrll call-graph and the count of how many times the arc has been
762a6b7db3Sskrll traversed. FILENAME is the name of file IFP and is provided
772a6b7db3Sskrll for formatting error-messages only. */
782a6b7db3Sskrll
792a6b7db3Sskrll void
cg_read_rec(FILE * ifp,const char * filename)802a6b7db3Sskrll cg_read_rec (FILE *ifp, const char *filename)
812a6b7db3Sskrll {
822a6b7db3Sskrll bfd_vma from_pc, self_pc;
832a6b7db3Sskrll unsigned int count;
842a6b7db3Sskrll
852a6b7db3Sskrll if (gmon_io_read_vma (ifp, &from_pc)
862a6b7db3Sskrll || gmon_io_read_vma (ifp, &self_pc)
872a6b7db3Sskrll || gmon_io_read_32 (ifp, &count))
882a6b7db3Sskrll {
892a6b7db3Sskrll fprintf (stderr, _("%s: %s: unexpected end of file\n"),
902a6b7db3Sskrll whoami, filename);
912a6b7db3Sskrll done (1);
922a6b7db3Sskrll }
932a6b7db3Sskrll
942a6b7db3Sskrll DBG (SAMPLEDEBUG,
952a6b7db3Sskrll printf ("[cg_read_rec] frompc 0x%lx selfpc 0x%lx count %lu\n",
962a6b7db3Sskrll (unsigned long) from_pc, (unsigned long) self_pc,
972a6b7db3Sskrll (unsigned long) count));
982a6b7db3Sskrll /* Add this arc: */
992a6b7db3Sskrll cg_tally (from_pc, self_pc, count);
1002a6b7db3Sskrll }
1012a6b7db3Sskrll
1022a6b7db3Sskrll /* Write all the arcs in the call-graph to file OFP. FILENAME is
1032a6b7db3Sskrll the name of OFP and is provided for formatting error-messages
1042a6b7db3Sskrll only. */
1052a6b7db3Sskrll
1062a6b7db3Sskrll void
cg_write_arcs(FILE * ofp,const char * filename)1072a6b7db3Sskrll cg_write_arcs (FILE *ofp, const char *filename)
1082a6b7db3Sskrll {
1092a6b7db3Sskrll Arc *arc;
1102a6b7db3Sskrll Sym *sym;
1112a6b7db3Sskrll
1122a6b7db3Sskrll for (sym = symtab.base; sym < symtab.limit; sym++)
1132a6b7db3Sskrll {
1142a6b7db3Sskrll for (arc = sym->cg.children; arc; arc = arc->next_child)
1152a6b7db3Sskrll {
1162a6b7db3Sskrll if (gmon_io_write_8 (ofp, GMON_TAG_CG_ARC)
1172a6b7db3Sskrll || gmon_io_write_vma (ofp, arc->parent->addr)
1182a6b7db3Sskrll || gmon_io_write_vma (ofp, arc->child->addr)
1192a6b7db3Sskrll || gmon_io_write_32 (ofp, arc->count))
1202a6b7db3Sskrll {
1212a6b7db3Sskrll perror (filename);
1222a6b7db3Sskrll done (1);
1232a6b7db3Sskrll }
1242a6b7db3Sskrll DBG (SAMPLEDEBUG,
1252a6b7db3Sskrll printf ("[cg_write_arcs] frompc 0x%lx selfpc 0x%lx count %lu\n",
1262a6b7db3Sskrll (unsigned long) arc->parent->addr,
1272a6b7db3Sskrll (unsigned long) arc->child->addr, arc->count));
1282a6b7db3Sskrll }
1292a6b7db3Sskrll }
1302a6b7db3Sskrll }
131