12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295 |
- /* cg_print.c - Print routines for displaying call graphs.
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
- This file is part of GNU Binutils.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
- 02110-1301, USA. */
- #include "gprof.h"
- #include "libiberty.h"
- #include "filenames.h"
- #include "search_list.h"
- #include "source.h"
- #include "symtab.h"
- #include "cg_arcs.h"
- #include "cg_print.h"
- #include "hist.h"
- #include "utils.h"
- #include "corefile.h"
- /* Return value of comparison functions used to sort tables. */
- #define LESSTHAN -1
- #define EQUALTO 0
- #define GREATERTHAN 1
- static void print_header (void);
- static void print_cycle (Sym *);
- static int cmp_member (Sym *, Sym *);
- static void sort_members (Sym *);
- static void print_members (Sym *);
- static int cmp_arc (Arc *, Arc *);
- static void sort_parents (Sym *);
- static void print_parents (Sym *);
- static void sort_children (Sym *);
- static void print_children (Sym *);
- static void print_line (Sym *);
- static int cmp_name (const PTR, const PTR);
- static int cmp_arc_count (const PTR, const PTR);
- static int cmp_fun_nuses (const PTR, const PTR);
- static void order_and_dump_functions_by_arcs
- (Arc **, unsigned long, int, Arc **, unsigned long *);
- /* Declarations of automatically generated functions to output blurbs. */
- extern void bsd_callg_blurb (FILE * fp);
- extern void fsf_callg_blurb (FILE * fp);
- double print_time = 0.0;
- static void
- print_header ()
- {
- if (first_output)
- first_output = FALSE;
- else
- printf ("\f\n");
- if (!bsd_style_output)
- {
- if (print_descriptions)
- printf (_("\t\t Call graph (explanation follows)\n\n"));
- else
- printf (_("\t\t\tCall graph\n\n"));
- }
- printf (_("\ngranularity: each sample hit covers %ld byte(s)"),
- (long) hist_scale * (long) sizeof (UNIT));
- if (print_time > 0.0)
- printf (_(" for %.2f%% of %.2f seconds\n\n"),
- 100.0 / print_time, print_time / hz);
- else
- {
- printf (_(" no time propagated\n\n"));
- /* This doesn't hurt, since all the numerators will be 0.0. */
- print_time = 1.0;
- }
- if (bsd_style_output)
- {
- printf ("%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n",
- "", "", "", "", _("called"), _("total"), _("parents"));
- printf ("%-6.6s %5.5s %7.7s %11.11s %7.7s+%-7.7s %-8.8s\t%5.5s\n",
- _("index"),
- /* xgettext:no-c-format */
- _("%time"),
- _("self"), _("descendants"), _("called"), _("self"),
- _("name"), _("index"));
- printf ("%6.6s %5.5s %7.7s %11.11s %7.7s/%-7.7s %-8.8s\n",
- "", "", "", "", _("called"), _("total"), _("children"));
- printf ("\n");
- }
- else
- {
- printf (_("index %% time self children called name\n"));
- }
- }
- /* Print a cycle header. */
- static void
- print_cycle (Sym *cyc)
- {
- char buf[BUFSIZ];
- sprintf (buf, "[%d]", cyc->cg.index);
- printf (bsd_style_output
- ? "%-6.6s %5.1f %7.2f %11.2f %7lu"
- : "%-6.6s %5.1f %7.2f %7.2f %7lu", buf,
- 100 * (cyc->cg.prop.self + cyc->cg.prop.child) / print_time,
- cyc->cg.prop.self / hz, cyc->cg.prop.child / hz, cyc->ncalls);
- if (cyc->cg.self_calls != 0)
- printf ("+%-7lu", cyc->cg.self_calls);
- else
- printf (" %7.7s", "");
- printf (_(" <cycle %d as a whole> [%d]\n"), cyc->cg.cyc.num, cyc->cg.index);
- }
- /* Compare LEFT and RIGHT membmer. Major comparison key is
- CG.PROP.SELF+CG.PROP.CHILD, secondary key is NCALLS+CG.SELF_CALLS. */
- static int
- cmp_member (Sym *left, Sym *right)
- {
- double left_time = left->cg.prop.self + left->cg.prop.child;
- double right_time = right->cg.prop.self + right->cg.prop.child;
- unsigned long left_calls = left->ncalls + left->cg.self_calls;
- unsigned long right_calls = right->ncalls + right->cg.self_calls;
- if (left_time > right_time)
- return GREATERTHAN;
- if (left_time < right_time)
- return LESSTHAN;
- if (left_calls > right_calls)
- return GREATERTHAN;
- if (left_calls < right_calls)
- return LESSTHAN;
- return EQUALTO;
- }
- /* Sort members of a cycle. */
- static void
- sort_members (Sym *cyc)
- {
- Sym *todo, *doing, *prev;
- /* Detach cycle members from cyclehead,
- and insertion sort them back on. */
- todo = cyc->cg.cyc.next;
- cyc->cg.cyc.next = 0;
- for (doing = todo; doing != NULL; doing = todo)
- {
- todo = doing->cg.cyc.next;
- for (prev = cyc; prev->cg.cyc.next; prev = prev->cg.cyc.next)
- {
- if (cmp_member (doing, prev->cg.cyc.next) == GREATERTHAN)
- break;
- }
- doing->cg.cyc.next = prev->cg.cyc.next;
- prev->cg.cyc.next = doing;
- }
- }
- /* Print the members of a cycle. */
- static void
- print_members (Sym *cyc)
- {
- Sym *member;
- sort_members (cyc);
- for (member = cyc->cg.cyc.next; member; member = member->cg.cyc.next)
- {
- printf (bsd_style_output
- ? "%6.6s %5.5s %7.2f %11.2f %7lu"
- : "%6.6s %5.5s %7.2f %7.2f %7lu",
- "", "", member->cg.prop.self / hz, member->cg.prop.child / hz,
- member->ncalls);
- if (member->cg.self_calls != 0)
- printf ("+%-7lu", member->cg.self_calls);
- else
- printf (" %7.7s", "");
- printf (" ");
- print_name (member);
- printf ("\n");
- }
- }
- /* Compare two arcs to/from the same child/parent.
- - if one arc is a self arc, it's least.
- - if one arc is within a cycle, it's less than.
- - if both arcs are within a cycle, compare arc counts.
- - if neither arc is within a cycle, compare with
- time + child_time as major key
- arc count as minor key. */
- static int
- cmp_arc (Arc *left, Arc *right)
- {
- Sym *left_parent = left->parent;
- Sym *left_child = left->child;
- Sym *right_parent = right->parent;
- Sym *right_child = right->child;
- double left_time, right_time;
- DBG (TIMEDEBUG,
- printf ("[cmp_arc] ");
- print_name (left_parent);
- printf (" calls ");
- print_name (left_child);
- printf (" %f + %f %lu/%lu\n", left->time, left->child_time,
- left->count, left_child->ncalls);
- printf ("[cmp_arc] ");
- print_name (right_parent);
- printf (" calls ");
- print_name (right_child);
- printf (" %f + %f %lu/%lu\n", right->time, right->child_time,
- right->count, right_child->ncalls);
- printf ("\n");
- );
- if (left_parent == left_child)
- return LESSTHAN; /* Left is a self call. */
- if (right_parent == right_child)
- return GREATERTHAN; /* Right is a self call. */
- if (left_parent->cg.cyc.num != 0 && left_child->cg.cyc.num != 0
- && left_parent->cg.cyc.num == left_child->cg.cyc.num)
- {
- /* Left is a call within a cycle. */
- if (right_parent->cg.cyc.num != 0 && right_child->cg.cyc.num != 0
- && right_parent->cg.cyc.num == right_child->cg.cyc.num)
- {
- /* Right is a call within the cycle, too. */
- if (left->count < right->count)
- return LESSTHAN;
- if (left->count > right->count)
- return GREATERTHAN;
- return EQUALTO;
- }
- else
- {
- /* Right isn't a call within the cycle. */
- return LESSTHAN;
- }
- }
- else
- {
- /* Left isn't a call within a cycle. */
- if (right_parent->cg.cyc.num != 0 && right_child->cg.cyc.num != 0
- && right_parent->cg.cyc.num == right_child->cg.cyc.num)
- {
- /* Right is a call within a cycle. */
- return GREATERTHAN;
- }
- else
- {
- /* Neither is a call within a cycle. */
- left_time = left->time + left->child_time;
- right_time = right->time + right->child_time;
- if (left_time < right_time)
- return LESSTHAN;
- if (left_time > right_time)
- return GREATERTHAN;
- if (left->count < right->count)
- return LESSTHAN;
- if (left->count > right->count)
- return GREATERTHAN;
- return EQUALTO;
- }
- }
- }
- static void
- sort_parents (Sym * child)
- {
- Arc *arc, *detached, sorted, *prev;
- /* Unlink parents from child, then insertion sort back on to
- sorted's parents.
- *arc the arc you have detached and are inserting.
- *detached the rest of the arcs to be sorted.
- sorted arc list onto which you insertion sort.
- *prev arc before the arc you are comparing. */
- sorted.next_parent = 0;
- for (arc = child->cg.parents; arc; arc = detached)
- {
- detached = arc->next_parent;
- /* Consider *arc as disconnected; insert it into sorted. */
- for (prev = &sorted; prev->next_parent; prev = prev->next_parent)
- {
- if (cmp_arc (arc, prev->next_parent) != GREATERTHAN)
- break;
- }
- arc->next_parent = prev->next_parent;
- prev->next_parent = arc;
- }
- /* Reattach sorted arcs to child. */
- child->cg.parents = sorted.next_parent;
- }
- static void
- print_parents (Sym *child)
- {
- Sym *parent;
- Arc *arc;
- Sym *cycle_head;
- if (child->cg.cyc.head != 0)
- cycle_head = child->cg.cyc.head;
- else
- cycle_head = child;
- if (!child->cg.parents)
- {
- printf (bsd_style_output
- ? _("%6.6s %5.5s %7.7s %11.11s %7.7s %7.7s <spontaneous>\n")
- : _("%6.6s %5.5s %7.7s %7.7s %7.7s %7.7s <spontaneous>\n"),
- "", "", "", "", "", "");
- return;
- }
- sort_parents (child);
- for (arc = child->cg.parents; arc; arc = arc->next_parent)
- {
- parent = arc->parent;
- if (child == parent || (child->cg.cyc.num != 0
- && parent->cg.cyc.num == child->cg.cyc.num))
- {
- /* Selfcall or call among siblings. */
- printf (bsd_style_output
- ? "%6.6s %5.5s %7.7s %11.11s %7lu %7.7s "
- : "%6.6s %5.5s %7.7s %7.7s %7lu %7.7s ",
- "", "", "", "",
- arc->count, "");
- print_name (parent);
- printf ("\n");
- }
- else
- {
- /* Regular parent of child. */
- printf (bsd_style_output
- ? "%6.6s %5.5s %7.2f %11.2f %7lu/%-7lu "
- : "%6.6s %5.5s %7.2f %7.2f %7lu/%-7lu ",
- "", "",
- arc->time / hz, arc->child_time / hz,
- arc->count, cycle_head->ncalls);
- print_name (parent);
- printf ("\n");
- }
- }
- }
- static void
- sort_children (Sym *parent)
- {
- Arc *arc, *detached, sorted, *prev;
- /* Unlink children from parent, then insertion sort back on to
- sorted's children.
- *arc the arc you have detached and are inserting.
- *detached the rest of the arcs to be sorted.
- sorted arc list onto which you insertion sort.
- *prev arc before the arc you are comparing. */
- sorted.next_child = 0;
- for (arc = parent->cg.children; arc; arc = detached)
- {
- detached = arc->next_child;
- /* Consider *arc as disconnected; insert it into sorted. */
- for (prev = &sorted; prev->next_child; prev = prev->next_child)
- {
- if (cmp_arc (arc, prev->next_child) != LESSTHAN)
- break;
- }
- arc->next_child = prev->next_child;
- prev->next_child = arc;
- }
- /* Reattach sorted children to parent. */
- parent->cg.children = sorted.next_child;
- }
- static void
- print_children (Sym *parent)
- {
- Sym *child;
- Arc *arc;
- sort_children (parent);
- arc = parent->cg.children;
- for (arc = parent->cg.children; arc; arc = arc->next_child)
- {
- child = arc->child;
- if (child == parent || (child->cg.cyc.num != 0
- && child->cg.cyc.num == parent->cg.cyc.num))
- {
- /* Self call or call to sibling. */
- printf (bsd_style_output
- ? "%6.6s %5.5s %7.7s %11.11s %7lu %7.7s "
- : "%6.6s %5.5s %7.7s %7.7s %7lu %7.7s ",
- "", "", "", "", arc->count, "");
- print_name (child);
- printf ("\n");
- }
- else
- {
- /* Regular child of parent. */
- printf (bsd_style_output
- ? "%6.6s %5.5s %7.2f %11.2f %7lu/%-7lu "
- : "%6.6s %5.5s %7.2f %7.2f %7lu/%-7lu ",
- "", "",
- arc->time / hz, arc->child_time / hz,
- arc->count, child->cg.cyc.head->ncalls);
- print_name (child);
- printf ("\n");
- }
- }
- }
- static void
- print_line (Sym *np)
- {
- char buf[BUFSIZ];
- sprintf (buf, "[%d]", np->cg.index);
- printf (bsd_style_output
- ? "%-6.6s %5.1f %7.2f %11.2f"
- : "%-6.6s %5.1f %7.2f %7.2f", buf,
- 100 * (np->cg.prop.self + np->cg.prop.child) / print_time,
- np->cg.prop.self / hz, np->cg.prop.child / hz);
- if ((np->ncalls + np->cg.self_calls) != 0)
- {
- printf (" %7lu", np->ncalls);
- if (np->cg.self_calls != 0)
- printf ("+%-7lu ", np->cg.self_calls);
- else
- printf (" %7.7s ", "");
- }
- else
- {
- printf (" %7.7s %7.7s ", "", "");
- }
- print_name (np);
- printf ("\n");
- }
- /* Print dynamic call graph. */
- void
- cg_print (Sym ** timesortsym)
- {
- unsigned int sym_index;
- Sym *parent;
- if (print_descriptions && bsd_style_output)
- bsd_callg_blurb (stdout);
- print_header ();
- for (sym_index = 0; sym_index < symtab.len + num_cycles; ++sym_index)
- {
- parent = timesortsym[sym_index];
- if ((ignore_zeros && parent->ncalls == 0
- && parent->cg.self_calls == 0 && parent->cg.prop.self == 0
- && parent->cg.prop.child == 0)
- || !parent->cg.print_flag
- || (line_granularity && ! parent->is_func))
- continue;
- if (!parent->name && parent->cg.cyc.num != 0)
- {
- /* Cycle header. */
- print_cycle (parent);
- print_members (parent);
- }
- else
- {
- print_parents (parent);
- print_line (parent);
- print_children (parent);
- }
- if (bsd_style_output)
- printf ("\n");
- printf ("-----------------------------------------------\n");
- if (bsd_style_output)
- printf ("\n");
- }
- free (timesortsym);
- if (print_descriptions && !bsd_style_output)
- fsf_callg_blurb (stdout);
- }
- static int
- cmp_name (const PTR left, const PTR right)
- {
- const Sym **npp1 = (const Sym **) left;
- const Sym **npp2 = (const Sym **) right;
- return strcmp ((*npp1)->name, (*npp2)->name);
- }
- void
- cg_print_index ()
- {
- unsigned int sym_index;
- unsigned int nnames, todo, i, j;
- int col, starting_col;
- Sym **name_sorted_syms, *sym;
- const char *filename;
- char buf[20];
- int column_width = (output_width - 1) / 3; /* Don't write in last col! */
- /* Now, sort regular function name
- alphabetically to create an index. */
- name_sorted_syms = (Sym **) xmalloc ((symtab.len + num_cycles) * sizeof (Sym *));
- for (sym_index = 0, nnames = 0; sym_index < symtab.len; sym_index++)
- {
- if (ignore_zeros && symtab.base[sym_index].ncalls == 0
- && symtab.base[sym_index].hist.time == 0)
- continue;
- name_sorted_syms[nnames++] = &symtab.base[sym_index];
- }
- qsort (name_sorted_syms, nnames, sizeof (Sym *), cmp_name);
- for (sym_index = 1, todo = nnames; sym_index <= num_cycles; sym_index++)
- name_sorted_syms[todo++] = &cycle_header[sym_index];
- printf ("\f\n");
- printf (_("Index by function name\n\n"));
- sym_index = (todo + 2) / 3;
- for (i = 0; i < sym_index; i++)
- {
- col = 0;
- starting_col = 0;
- for (j = i; j < todo; j += sym_index)
- {
- sym = name_sorted_syms[j];
- if (sym->cg.print_flag)
- sprintf (buf, "[%d]", sym->cg.index);
- else
- sprintf (buf, "(%d)", sym->cg.index);
- if (j < nnames)
- {
- if (bsd_style_output)
- {
- printf ("%6.6s %-19.19s", buf, sym->name);
- }
- else
- {
- col += strlen (buf);
- for (; col < starting_col + 5; ++col)
- putchar (' ');
- printf (" %s ", buf);
- col += print_name_only (sym);
- if (!line_granularity && sym->is_static && sym->file)
- {
- filename = sym->file->name;
- if (!print_path)
- {
- filename = strrchr (filename, '/');
- if (filename)
- ++filename;
- else
- filename = sym->file->name;
- }
- printf (" (%s)", filename);
- col += strlen (filename) + 3;
- }
- }
- }
- else
- {
- if (bsd_style_output)
- {
- printf ("%6.6s ", buf);
- sprintf (buf, _("<cycle %d>"), sym->cg.cyc.num);
- printf ("%-19.19s", buf);
- }
- else
- {
- col += strlen (buf);
- for (; col < starting_col + 5; ++col)
- putchar (' ');
- printf (" %s ", buf);
- sprintf (buf, _("<cycle %d>"), sym->cg.cyc.num);
- printf ("%s", buf);
- col += strlen (buf);
- }
- }
- starting_col += column_width;
- }
- printf ("\n");
- }
- free (name_sorted_syms);
- }
- /* Compare two arcs based on their usage counts.
- We want to sort in descending order. */
- static int
- cmp_arc_count (const PTR left, const PTR right)
- {
- const Arc **npp1 = (const Arc **) left;
- const Arc **npp2 = (const Arc **) right;
- if ((*npp1)->count > (*npp2)->count)
- return -1;
- else if ((*npp1)->count < (*npp2)->count)
- return 1;
- else
- return 0;
- }
- /* Compare two funtions based on their usage counts.
- We want to sort in descending order. */
- static int
- cmp_fun_nuses (const PTR left, const PTR right)
- {
- const Sym **npp1 = (const Sym **) left;
- const Sym **npp2 = (const Sym **) right;
- if ((*npp1)->nuses > (*npp2)->nuses)
- return -1;
- else if ((*npp1)->nuses < (*npp2)->nuses)
- return 1;
- else
- return 0;
- }
- /* Print a suggested function ordering based on the profiling data.
- We perform 4 major steps when ordering functions:
- * Group unused functions together and place them at the
- end of the function order.
- * Search the highest use arcs (those which account for 90% of
- the total arc count) for functions which have several parents.
- Group those with the most call sites together (currently the
- top 1.25% which have at least five different call sites).
- These are emitted at the start of the function order.
- * Use a greedy placement algorithm to place functions which
- occur in the top 99% of the arcs in the profile. Some provisions
- are made to handle high usage arcs where the parent and/or
- child has already been placed.
- * Run the same greedy placement algorithm on the remaining
- arcs to place the leftover functions.
- The various "magic numbers" should (one day) be tuneable by command
- line options. They were arrived at by benchmarking a few applications
- with various values to see which values produced better overall function
- orderings.
- Of course, profiling errors, machine limitations (PA long calls), and
- poor cutoff values for the placement algorithm may limit the usefullness
- of the resulting function order. Improvements would be greatly appreciated.
- Suggestions:
- * Place the functions with many callers near the middle of the
- list to reduce long calls.
- * Propagate arc usage changes as functions are placed. Ie if
- func1 and func2 are placed together, arcs to/from those arcs
- to the same parent/child should be combined, then resort the
- arcs to choose the next one.
- * Implement some global positioning algorithm to place the
- chains made by the greedy local positioning algorithm. Probably
- by examining arcs which haven't been placed yet to tie two
- chains together.
- * Take a function's size and time into account in the algorithm;
- size in particular is important on the PA (long calls). Placing
- many small functions onto their own page may be wise.
- * Use better profiling information; many published algorithms
- are based on call sequences through time, rather than just
- arc counts.
- * Prodecure cloning could improve performance when a small number
- of arcs account for most of the calls to a particular function.
- * Use relocation information to avoid moving unused functions
- completely out of the code stream; this would avoid severe lossage
- when the profile data bears little resemblance to actual runs.
- * Propagation of arc usages should also improve .o link line
- ordering which shares the same arc placement algorithm with
- the function ordering code (in fact it is a degenerate case
- of function ordering). */
- void
- cg_print_function_ordering (void)
- {
- unsigned long sym_index;
- unsigned long arc_index;
- unsigned long used, unused, scratch_index;
- unsigned long unplaced_arc_count, high_arc_count, scratch_arc_count;
- #ifdef __GNUC__
- unsigned long long total_arcs, tmp_arcs_count;
- #else
- unsigned long total_arcs, tmp_arcs_count;
- #endif
- Sym **unused_syms, **used_syms, **scratch_syms;
- Arc **unplaced_arcs, **high_arcs, **scratch_arcs;
- sym_index = 0;
- used = 0;
- unused = 0;
- scratch_index = 0;
- unplaced_arc_count = 0;
- high_arc_count = 0;
- scratch_arc_count = 0;
- /* First group all the unused functions together. */
- unused_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
- used_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
- scratch_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
- high_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- scratch_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- unplaced_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- /* Walk through all the functions; mark those which are never
- called as placed (we'll emit them as a group later). */
- for (sym_index = 0, used = 0, unused = 0; sym_index < symtab.len; sym_index++)
- {
- if (symtab.base[sym_index].ncalls == 0)
- {
- unused_syms[unused++] = &symtab.base[sym_index];
- symtab.base[sym_index].has_been_placed = 1;
- }
- else
- {
- used_syms[used++] = &symtab.base[sym_index];
- symtab.base[sym_index].has_been_placed = 0;
- symtab.base[sym_index].next = 0;
- symtab.base[sym_index].prev = 0;
- symtab.base[sym_index].nuses = 0;
- }
- }
- /* Sort the arcs from most used to least used. */
- qsort (arcs, numarcs, sizeof (Arc *), cmp_arc_count);
- /* Compute the total arc count. Also mark arcs as unplaced.
- Note we don't compensate for overflow if that happens!
- Overflow is much less likely when this file is compiled
- with GCC as it can double-wide integers via long long. */
- total_arcs = 0;
- for (arc_index = 0; arc_index < numarcs; arc_index++)
- {
- total_arcs += arcs[arc_index]->count;
- arcs[arc_index]->has_been_placed = 0;
- }
- /* We want to pull out those functions which are referenced
- by many highly used arcs and emit them as a group. This
- could probably use some tuning. */
- tmp_arcs_count = 0;
- for (arc_index = 0; arc_index < numarcs; arc_index++)
- {
- tmp_arcs_count += arcs[arc_index]->count;
- /* Count how many times each parent and child are used up
- to our threshhold of arcs (90%). */
- if ((double)tmp_arcs_count / (double)total_arcs > 0.90)
- break;
- arcs[arc_index]->child->nuses++;
- }
- /* Now sort a temporary symbol table based on the number of
- times each function was used in the highest used arcs. */
- memcpy (scratch_syms, used_syms, used * sizeof (Sym *));
- qsort (scratch_syms, used, sizeof (Sym *), cmp_fun_nuses);
- /* Now pick out those symbols we're going to emit as
- a group. We take up to 1.25% of the used symbols. */
- for (sym_index = 0; sym_index < used / 80; sym_index++)
- {
- Sym *sym = scratch_syms[sym_index];
- Arc *arc;
- /* If we hit symbols that aren't used from many call sites,
- then we can quit. We choose five as the low limit for
- no particular reason. */
- if (sym->nuses == 5)
- break;
- /* We're going to need the arcs between these functions.
- Unfortunately, we don't know all these functions
- until we're done. So we keep track of all the arcs
- to the functions we care about, then prune out those
- which are uninteresting.
- An interesting variation would be to quit when we found
- multi-call site functions which account for some percentage
- of the arcs. */
- arc = sym->cg.children;
- while (arc)
- {
- if (arc->parent != arc->child)
- scratch_arcs[scratch_arc_count++] = arc;
- arc->has_been_placed = 1;
- arc = arc->next_child;
- }
- arc = sym->cg.parents;
- while (arc)
- {
- if (arc->parent != arc->child)
- scratch_arcs[scratch_arc_count++] = arc;
- arc->has_been_placed = 1;
- arc = arc->next_parent;
- }
- /* Keep track of how many symbols we're going to place. */
- scratch_index = sym_index;
- /* A lie, but it makes identifying
- these functions easier later. */
- sym->has_been_placed = 1;
- }
- /* Now walk through the temporary arcs and copy
- those we care about into the high arcs array. */
- for (arc_index = 0; arc_index < scratch_arc_count; arc_index++)
- {
- Arc *arc = scratch_arcs[arc_index];
- /* If this arc refers to highly used functions, then
- then we want to keep it. */
- if (arc->child->has_been_placed
- && arc->parent->has_been_placed)
- {
- high_arcs[high_arc_count++] = scratch_arcs[arc_index];
- /* We need to turn of has_been_placed since we're going to
- use the main arc placement algorithm on these arcs. */
- arc->child->has_been_placed = 0;
- arc->parent->has_been_placed = 0;
- }
- }
- /* Dump the multi-site high usage functions which are not
- going to be ordered by the main ordering algorithm. */
- for (sym_index = 0; sym_index < scratch_index; sym_index++)
- {
- if (scratch_syms[sym_index]->has_been_placed)
- printf ("%s\n", scratch_syms[sym_index]->name);
- }
- /* Now we can order the multi-site high use
- functions based on the arcs between them. */
- qsort (high_arcs, high_arc_count, sizeof (Arc *), cmp_arc_count);
- order_and_dump_functions_by_arcs (high_arcs, high_arc_count, 1,
- unplaced_arcs, &unplaced_arc_count);
- /* Order and dump the high use functions left,
- these typically have only a few call sites. */
- order_and_dump_functions_by_arcs (arcs, numarcs, 0,
- unplaced_arcs, &unplaced_arc_count);
- /* Now place the rarely used functions. */
- order_and_dump_functions_by_arcs (unplaced_arcs, unplaced_arc_count, 1,
- scratch_arcs, &scratch_arc_count);
- /* Output any functions not emitted by the order_and_dump calls. */
- for (sym_index = 0; sym_index < used; sym_index++)
- if (used_syms[sym_index]->has_been_placed == 0)
- printf("%s\n", used_syms[sym_index]->name);
- /* Output the unused functions. */
- for (sym_index = 0; sym_index < unused; sym_index++)
- printf("%s\n", unused_syms[sym_index]->name);
- unused_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
- used_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
- scratch_syms = (Sym **) xmalloc (symtab.len * sizeof (Sym *));
- high_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- scratch_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- unplaced_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- free (unused_syms);
- free (used_syms);
- free (scratch_syms);
- free (high_arcs);
- free (scratch_arcs);
- free (unplaced_arcs);
- }
- /* Place functions based on the arcs in THE_ARCS with ARC_COUNT entries;
- place unused arcs into UNPLACED_ARCS/UNPLACED_ARC_COUNT.
- If ALL is nonzero, then place all functions referenced by THE_ARCS,
- else only place those referenced in the top 99% of the arcs in THE_ARCS. */
- #define MOST 0.99
- static void
- order_and_dump_functions_by_arcs (the_arcs, arc_count, all,
- unplaced_arcs, unplaced_arc_count)
- Arc **the_arcs;
- unsigned long arc_count;
- int all;
- Arc **unplaced_arcs;
- unsigned long *unplaced_arc_count;
- {
- #ifdef __GNUC__
- unsigned long long tmp_arcs, total_arcs;
- #else
- unsigned long tmp_arcs, total_arcs;
- #endif
- unsigned int arc_index;
- /* If needed, compute the total arc count.
- Note we don't compensate for overflow if that happens! */
- if (! all)
- {
- total_arcs = 0;
- for (arc_index = 0; arc_index < arc_count; arc_index++)
- total_arcs += the_arcs[arc_index]->count;
- }
- else
- total_arcs = 0;
- tmp_arcs = 0;
- for (arc_index = 0; arc_index < arc_count; arc_index++)
- {
- Sym *sym1, *sym2;
- Sym *child, *parent;
- tmp_arcs += the_arcs[arc_index]->count;
- /* Ignore this arc if it's already been placed. */
- if (the_arcs[arc_index]->has_been_placed)
- continue;
- child = the_arcs[arc_index]->child;
- parent = the_arcs[arc_index]->parent;
- /* If we're not using all arcs, and this is a rarely used
- arc, then put it on the unplaced_arc list. Similarly
- if both the parent and child of this arc have been placed. */
- if ((! all && (double)tmp_arcs / (double)total_arcs > MOST)
- || child->has_been_placed || parent->has_been_placed)
- {
- unplaced_arcs[(*unplaced_arc_count)++] = the_arcs[arc_index];
- continue;
- }
- /* If all slots in the parent and child are full, then there isn't
- anything we can do right now. We'll place this arc on the
- unplaced arc list in the hope that a global positioning
- algorithm can use it to place function chains. */
- if (parent->next && parent->prev && child->next && child->prev)
- {
- unplaced_arcs[(*unplaced_arc_count)++] = the_arcs[arc_index];
- continue;
- }
- /* If the parent is unattached, then find the closest
- place to attach it onto child's chain. Similarly
- for the opposite case. */
- if (!parent->next && !parent->prev)
- {
- int next_count = 0;
- int prev_count = 0;
- Sym *prev = child;
- Sym *next = child;
- /* Walk to the beginning and end of the child's chain. */
- while (next->next)
- {
- next = next->next;
- next_count++;
- }
- while (prev->prev)
- {
- prev = prev->prev;
- prev_count++;
- }
- /* Choose the closest. */
- child = next_count < prev_count ? next : prev;
- }
- else if (! child->next && !child->prev)
- {
- int next_count = 0;
- int prev_count = 0;
- Sym *prev = parent;
- Sym *next = parent;
- while (next->next)
- {
- next = next->next;
- next_count++;
- }
- while (prev->prev)
- {
- prev = prev->prev;
- prev_count++;
- }
- parent = prev_count < next_count ? prev : next;
- }
- else
- {
- /* Couldn't find anywhere to attach the functions,
- put the arc on the unplaced arc list. */
- unplaced_arcs[(*unplaced_arc_count)++] = the_arcs[arc_index];
- continue;
- }
- /* Make sure we don't tie two ends together. */
- sym1 = parent;
- if (sym1->next)
- while (sym1->next)
- sym1 = sym1->next;
- else
- while (sym1->prev)
- sym1 = sym1->prev;
- sym2 = child;
- if (sym2->next)
- while (sym2->next)
- sym2 = sym2->next;
- else
- while (sym2->prev)
- sym2 = sym2->prev;
- if (sym1 == child
- && sym2 == parent)
- {
- /* This would tie two ends together. */
- unplaced_arcs[(*unplaced_arc_count)++] = the_arcs[arc_index];
- continue;
- }
- if (parent->next)
- {
- /* Must attach to the parent's prev field. */
- if (! child->next)
- {
- /* parent-prev and child-next */
- parent->prev = child;
- child->next = parent;
- the_arcs[arc_index]->has_been_placed = 1;
- }
- }
- else if (parent->prev)
- {
- /* Must attach to the parent's next field. */
- if (! child->prev)
- {
- /* parent-next and child-prev */
- parent->next = child;
- child->prev = parent;
- the_arcs[arc_index]->has_been_placed = 1;
- }
- }
- else
- {
- /* Can attach to either field in the parent, depends
- on where we've got space in the child. */
- if (child->prev)
- {
- /* parent-prev and child-next. */
- parent->prev = child;
- child->next = parent;
- the_arcs[arc_index]->has_been_placed = 1;
- }
- else
- {
- /* parent-next and child-prev. */
- parent->next = child;
- child->prev = parent;
- the_arcs[arc_index]->has_been_placed = 1;
- }
- }
- }
- /* Dump the chains of functions we've made. */
- for (arc_index = 0; arc_index < arc_count; arc_index++)
- {
- Sym *sym;
- if (the_arcs[arc_index]->parent->has_been_placed
- || the_arcs[arc_index]->child->has_been_placed)
- continue;
- sym = the_arcs[arc_index]->parent;
- /* If this symbol isn't attached to any other
- symbols, then we've got a rarely used arc.
- Skip it for now, we'll deal with them later. */
- if (sym->next == NULL
- && sym->prev == NULL)
- continue;
- /* Get to the start of this chain. */
- while (sym->prev)
- sym = sym->prev;
- while (sym)
- {
- /* Mark it as placed. */
- sym->has_been_placed = 1;
- printf ("%s\n", sym->name);
- sym = sym->next;
- }
- }
- /* If we want to place all the arcs, then output
- those which weren't placed by the main algorithm. */
- if (all)
- for (arc_index = 0; arc_index < arc_count; arc_index++)
- {
- Sym *sym;
- if (the_arcs[arc_index]->parent->has_been_placed
- || the_arcs[arc_index]->child->has_been_placed)
- continue;
- sym = the_arcs[arc_index]->parent;
- sym->has_been_placed = 1;
- printf ("%s\n", sym->name);
- }
- }
- /* Compare two function_map structs based on file name.
- We want to sort in ascending order. */
- static int
- cmp_symbol_map (const void * l, const void * r)
- {
- return filename_cmp (((struct function_map *) l)->file_name,
- ((struct function_map *) r)->file_name);
- }
- /* Print a suggested .o ordering for files on a link line based
- on profiling information. This uses the function placement
- code for the bulk of its work. */
- void
- cg_print_file_ordering (void)
- {
- unsigned long scratch_arc_count;
- unsigned long arc_index;
- unsigned long sym_index;
- Arc **scratch_arcs;
- char *last;
- scratch_arc_count = 0;
- scratch_arcs = (Arc **) xmalloc (numarcs * sizeof (Arc *));
- for (arc_index = 0; arc_index < numarcs; arc_index++)
- {
- if (! arcs[arc_index]->parent->mapped
- || ! arcs[arc_index]->child->mapped)
- arcs[arc_index]->has_been_placed = 1;
- }
- order_and_dump_functions_by_arcs (arcs, numarcs, 0,
- scratch_arcs, &scratch_arc_count);
- /* Output .o's not handled by the main placement algorithm. */
- for (sym_index = 0; sym_index < symtab.len; sym_index++)
- {
- if (symtab.base[sym_index].mapped
- && ! symtab.base[sym_index].has_been_placed)
- printf ("%s\n", symtab.base[sym_index].name);
- }
- qsort (symbol_map, symbol_map_count, sizeof (struct function_map), cmp_symbol_map);
- /* Now output any .o's that didn't have any text symbols. */
- last = NULL;
- for (sym_index = 0; sym_index < symbol_map_count; sym_index++)
- {
- unsigned int index2;
- /* Don't bother searching if this symbol
- is the same as the previous one. */
- if (last && !filename_cmp (last, symbol_map[sym_index].file_name))
- continue;
- for (index2 = 0; index2 < symtab.len; index2++)
- {
- if (! symtab.base[index2].mapped)
- continue;
- if (!filename_cmp (symtab.base[index2].name,
- symbol_map[sym_index].file_name))
- break;
- }
- /* If we didn't find it in the symbol table, then it must
- be a .o with no text symbols. Output it last. */
- if (index2 == symtab.len)
- printf ("%s\n", symbol_map[sym_index].file_name);
- last = symbol_map[sym_index].file_name;
- }
- }
|