1 /* Call stacks at program points.
2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
3 Contributed by David Malcolm <dmalcolm@redhat.com>.
5 This file is part of GCC.
7 GCC is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3, or (at your option)
12 GCC is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GCC; see the file COPYING3. If not see
19 <http://www.gnu.org/licenses/>. */
22 #define INCLUDE_MEMORY
24 #include "coretypes.h"
25 #include "pretty-print.h"
28 #include "ordered-hash-map.h"
33 #include "basic-block.h"
35 #include "gimple-iterator.h"
37 #include "analyzer/analyzer.h"
38 #include "analyzer/analyzer-logging.h"
39 #include "analyzer/call-string.h"
40 #include "analyzer/supergraph.h"
45 #pragma GCC diagnostic ignored "-Wformat-diag"
48 /* class call_string. */
50 /* struct call_string::element_t. */
52 /* call_string::element_t's equality operator. */
55 call_string::element_t::operator== (const call_string::element_t
&other
) const
57 return (m_caller
== other
.m_caller
&& m_callee
== other
.m_callee
);
60 /* call_string::element_t's inequality operator. */
62 call_string::element_t::operator!= (const call_string::element_t
&other
) const
64 return !(*this == other
);
68 call_string::element_t::get_caller_function () const
70 return m_caller
->get_function ();
74 call_string::element_t::get_callee_function () const
76 return m_callee
->get_function ();
79 /* Print this to PP. */
82 call_string::print (pretty_printer
*pp
) const
86 call_string::element_t
*e
;
88 FOR_EACH_VEC_ELT (m_elements
, i
, e
)
92 pp_printf (pp
, "(SN: %i -> SN: %i in %s)",
93 e
->m_callee
->m_index
, e
->m_caller
->m_index
,
94 function_name (e
->m_caller
->m_fun
));
100 /* Return a new json::array of the form
101 [{"src_snode_idx" : int,
102 "dst_snode_idx" : int,
104 ...for each element in the callstring]. */
107 call_string::to_json () const
109 json::array
*arr
= new json::array ();
111 for (const call_string::element_t
&e
: m_elements
)
113 json::object
*e_obj
= new json::object ();
114 e_obj
->set_integer ("src_snode_idx", e
.m_callee
->m_index
);
115 e_obj
->set_integer ("dst_snode_idx", e
.m_caller
->m_index
);
116 e_obj
->set_string ("funcname", function_name (e
.m_caller
->m_fun
));
123 /* Get or create the call_string resulting from pushing the return
124 superedge for CALL_SEDGE onto the end of this call_string. */
127 call_string::push_call (const supergraph
&sg
,
128 const call_superedge
*call_sedge
) const
130 gcc_assert (call_sedge
);
131 const return_superedge
*return_sedge
= call_sedge
->get_edge_for_return (sg
);
132 gcc_assert (return_sedge
);
133 return push_call (return_sedge
->m_dest
, return_sedge
->m_src
);
136 /* Get or create the call_string resulting from pushing the call
137 (caller, callee) onto the end of this call_string. */
140 call_string::push_call (const supernode
*caller
,
141 const supernode
*callee
) const
143 call_string::element_t
e (caller
, callee
);
145 if (const call_string
**slot
= m_children
.get (e
))
148 call_string
*result
= new call_string (*this, e
);
149 m_children
.put (e
, result
);
153 /* Count the number of times the top-most call site appears in the
156 call_string::calc_recursion_depth () const
158 if (m_elements
.is_empty ())
160 const call_string::element_t top_return_sedge
161 = m_elements
[m_elements
.length () - 1];
164 for (const call_string::element_t
&e
: m_elements
)
165 if (e
== top_return_sedge
)
170 /* Count the number of times FUN appears in the string. */
173 call_string::count_occurrences_of_function (function
*fun
) const
176 for (const call_string::element_t
&e
: m_elements
)
178 if (e
.get_callee_function () == fun
)
180 if (e
.get_caller_function () == fun
)
186 /* Comparator for call strings.
187 This implements a version of lexicographical order.
188 Return negative if A is before B.
189 Return positive if B is after A.
190 Return 0 if they are equal. */
193 call_string::cmp (const call_string
&a
,
194 const call_string
&b
)
196 unsigned len_a
= a
.length ();
197 unsigned len_b
= b
.length ();
202 /* Consider index i; the strings have been equal up to it. */
204 /* Have both strings run out? */
205 if (i
>= len_a
&& i
>= len_b
)
208 /* Otherwise, has just one of the strings run out? */
214 /* Otherwise, compare the node pairs. */
215 const call_string::element_t a_node_pair
= a
[i
];
216 const call_string::element_t b_node_pair
= b
[i
];
218 = a_node_pair
.m_callee
->m_index
- b_node_pair
.m_callee
->m_index
;
222 = a_node_pair
.m_caller
->m_index
- b_node_pair
.m_caller
->m_index
;
226 // TODO: test coverage for this
230 /* Comparator for use by vec<const call_string *>::qsort. */
233 call_string::cmp_ptr_ptr (const void *pa
, const void *pb
)
235 const call_string
*cs_a
= *static_cast <const call_string
* const *> (pa
);
236 const call_string
*cs_b
= *static_cast <const call_string
* const *> (pb
);
237 return cmp (*cs_a
, *cs_b
);
240 /* Return the pointer to callee of the topmost call in the stack,
241 or NULL if stack is empty. */
243 call_string::get_callee_node () const
245 if(m_elements
.is_empty ())
247 return m_elements
[m_elements
.length () - 1].m_callee
;
250 /* Return the pointer to caller of the topmost call in the stack,
251 or NULL if stack is empty. */
253 call_string::get_caller_node () const
255 if(m_elements
.is_empty ())
257 return m_elements
[m_elements
.length () - 1].m_caller
;
260 /* Assert that this object is sane. */
263 call_string::validate () const
265 /* Skip this in a release build. */
270 gcc_assert (m_parent
|| m_elements
.length () == 0);
272 /* Each entry's "caller" should be the "callee" of the previous entry. */
273 call_string::element_t
*e
;
275 FOR_EACH_VEC_ELT (m_elements
, i
, e
)
277 gcc_assert (e
->get_caller_function () ==
278 m_elements
[i
- 1].get_callee_function ());
281 /* ctor for the root/empty call_string. */
283 call_string::call_string ()
284 : m_parent (NULL
), m_elements ()
288 /* ctor for a child call_string. */
290 call_string::call_string (const call_string
&parent
, const element_t
&to_push
)
291 : m_parent (&parent
),
292 m_elements (parent
.m_elements
.length () + 1)
294 m_elements
.splice (parent
.m_elements
);
295 m_elements
.quick_push (to_push
);
298 /* dtor for call_string: recursively delete children. */
300 call_string::~call_string ()
302 for (auto child_iter
: m_children
)
303 delete child_iter
.second
;
306 /* Log this call_string and all its descendents recursively to LOGGER,
307 using indentation and elision to highlight the hierarchy. */
310 call_string::recursive_log (logger
*logger
) const
312 logger
->start_log_line ();
313 pretty_printer
*pp
= logger
->get_printer ();
314 for (unsigned i
= 0; i
< length (); i
++)
319 /* Elide all but the final element, since they are shared with
320 the parent call_string. */
321 for (unsigned i
= 0; i
< length (); i
++)
322 pp_string (pp
, "..., ");
323 /* Log the final element in detail. */
324 const element_t
*e
= &m_elements
[m_elements
.length () - 1];
325 pp_printf (pp
, "(SN: %i -> SN: %i in %s)]",
326 e
->m_callee
->m_index
, e
->m_caller
->m_index
,
327 function_name (e
->m_caller
->m_fun
));
330 pp_string (pp
, "[]");
331 logger
->end_log_line ();
333 /* Recurse into children. */
335 auto_vec
<const call_string
*> children (m_children
.elements ());
336 for (auto iter
: m_children
)
337 children
.safe_push (iter
.second
);
338 children
.qsort (call_string::cmp_ptr_ptr
);
340 for (auto iter
: children
)
341 iter
->recursive_log (logger
);
345 #endif /* #if ENABLE_ANALYZER */