Coverage for pyTooling/CallGraph/__init__.py: 100%
219 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 14:08 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 14:08 +0000
1# ==================================================================================================================== #
2# _____ _ _ ____ _ _ ____ _ #
3# _ __ _ |_ _|__ ___ | (_)_ __ __ _ / ___|__ _| | |/ ___|_ __ __ _ _ __ | |__ #
4# | '_ \| | | || |/ _ \ / _ \| | | '_ \ / _` || | / _` | | | | _| '__/ _` | '_ \| '_ \ #
5# | |_) | |_| || | (_) | (_) | | | | | | (_| || |__| (_| | | | |_| | | | (_| | |_) | | | | #
6# | .__/ \__, ||_|\___/ \___/|_|_|_| |_|\__, (_)____\__,_|_|_|\____|_| \__,_| .__/|_| |_| #
7# |_| |___/ |___/ |_| #
8# ==================================================================================================================== #
9# Authors: #
10# Patrick Lehmann #
11# #
12# License: #
13# ==================================================================================================================== #
14# Copyright 2026-2026 Patrick Lehmann - Bötzingen, Germany #
15# #
16# Licensed under the Apache License, Version 2.0 (the "License"); #
17# you may not use this file except in compliance with the License. #
18# You may obtain a copy of the License at #
19# #
20# http://www.apache.org/licenses/LICENSE-2.0 #
21# #
22# Unless required by applicable law or agreed to in writing, software #
23# distributed under the License is distributed on an "AS IS" BASIS, #
24# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
25# See the License for the specific language governing permissions and #
26# limitations under the License. #
27# #
28# SPDX-License-Identifier: Apache-2.0 #
29# ==================================================================================================================== #
30#
31"""
32A data structure to describe the **call graph** of a program: its functions and the calls between them.
34A :class:`CallGraph` describes the functions of e.g. a program, a translation unit or a profiled run. A
35:class:`Function` is identified by an ID unique within its graph, e.g. a qualified or a mangled name. A :class:`Call`
36leads from the calling function to the called function, so a recursion is a cycle of the graph. A :class:`CallKind`
37says how the callee was determined, and a :class:`FunctionKind` if a function is defined in the described code, defined
38elsewhere, or a placeholder for unknown functions, e.g. the targets of an indirect call.
40Functions and calls carry an optional execution count, e.g. from a profiler, and an optional value of any type. A
41function can refer to its control flow graph, and a call to its call site: the basic block of the caller's control flow
42graph the call is made from. Call graphs, functions and calls carry key-value-pairs, e.g. the results of an analysis.
43:meth:`CallGraph.ToGraph` converts a call graph into a :class:`~pyTooling.Graph.Graph`.
45.. hint::
47 See :ref:`high-level help <STRUCT/CallGraph>` for explanations and usage examples.
49.. seealso::
51 :mod:`pyTooling.ControlFlow`
52 |rarr| The control flow graph of a function, whose blocks are the call sites.
53 :mod:`pyTooling.Graph`
54 |rarr| The graph data structure a call graph is converted into.
55 :mod:`pyTooling.Graph.GraphML`
56 |rarr| Writing a graph as a GraphML document.
57"""
58from __future__ import annotations
60from enum import Enum
61from typing import Any, Callable, Generator, Hashable, Mapping, Optional as Nullable
63from pyTooling.Common import getFullyQualifiedName
64from pyTooling.ControlFlow import Base, BaseWithCountAndValue, BasicBlock, BlockKind, ControlFlowGraph
65from pyTooling.Decorators import export, readonly
66from pyTooling.Exceptions import ToolingException
67from pyTooling.Graph import Graph, Vertex
68from pyTooling.MetaClasses import ExtendedType
71# A class with a property named like a class - ``ControlFlowGraph`` - can't name that class in the annotation of a
72# field: the class body's namespace, where annotations are evaluated, binds the name to the property.
73_ControlFlowGraph = ControlFlowGraph
76@export
77class FunctionKind(Enum):
78 """
79 Kind of a function: whether the call graph describes its code.
80 """
81 Defined = 0 #: The function is defined in the described code, e.g. in the translation unit or the profiled program.
82 External = 1 #: The function is defined elsewhere, e.g. a library function or a Python built-in function.
83 Unknown = 2 #: A placeholder for functions the source doesn't know, e.g. the targets of an unresolved indirect call.
85 def __str__(self) -> str:
86 """
87 Return the member's name.
89 :returns: Name of the member, e.g. ``External``.
90 """
91 return self.name
94@export
95class CallKind(Enum):
96 """
97 Kind of a call: how the callee is determined.
98 """
99 Default = 0 #: The call wasn't classified, e.g. a pair of caller and callee a profiler reports.
100 Direct = 1 #: The call names its callee, e.g. a C function called by name.
101 Indirect = 2 #: The callee is determined at run time, e.g. through a function pointer or a virtual method.
103 def __str__(self) -> str:
104 """
105 Return the member's name.
107 :returns: Name of the member, e.g. ``Indirect``.
108 """
109 return self.name
112@export
113class CallGraphError(ToolingException):
114 """Base-exception of all exceptions raised by :mod:`pyTooling.CallGraph`."""
117@export
118class DuplicateFunctionError(CallGraphError):
119 """The exception raised for a function with an ID, which the graph has already."""
122@export
123class Function(BaseWithCountAndValue):
124 """
125 A function of a call graph.
127 A function is identified by an ID unique within its graph, e.g. a qualified or a mangled name. Its outbound calls lead
128 to the functions it calls, its inbound calls come from the functions calling it. It can refer to its control flow
129 graph, whose blocks are the call sites of its outbound calls.
130 """
131 _callGraph: _CallGraph #: The call graph this function belongs to.
132 _id: Hashable #: ID of the function, unique within its graph.
133 _kind: FunctionKind #: Kind of the function.
134 _controlFlowGraph: Nullable[_ControlFlowGraph] #: The function's control flow graph, if it's known.
135 _inboundCalls: list[Call] #: Calls of this function.
136 _outboundCalls: list[Call] #: Calls this function makes.
138 def __init__(
139 self,
140 callGraph: _CallGraph,
141 functionID: Hashable,
142 *,
143 kind: FunctionKind = FunctionKind.Defined,
144 controlFlowGraph: Nullable[_ControlFlowGraph] = None,
145 count: Nullable[int] = None,
146 value: Nullable[Any] = None,
147 keyValuePairs: Nullable[Mapping[Hashable, Any]] = None
148 ) -> None:
149 """
150 Initializes a function and adds it to its graph.
152 :param callGraph: The call graph the function belongs to.
153 :param functionID: ID of the function, unique within its graph.
154 :param kind: Optional, kind of the function. Default: :attr:`FunctionKind.Defined`.
155 :param controlFlowGraph: Optional, the function's control flow graph. Default: ``None``.
156 :param count: Optional, how often the function was called. Default: ``None``.
157 :param value: Optional, any value attached to the function, e.g. its location. Default: ``None``.
158 :param keyValuePairs: Optional, mapping (dictionary) of key-value-pairs. Default: ``None``.
159 :raises TypeError: If parameter 'count' is not of type :class:`int`.
160 :raises ValueError: If parameter 'count' is negative.
161 :raises ValueError: If parameter 'callGraph' is None.
162 :raises TypeError: If parameter 'callGraph' is not of type :class:`CallGraph`.
163 :raises ValueError: If parameter 'functionID' is None.
164 :raises TypeError: If parameter 'functionID' is not hashable.
165 :raises DuplicateFunctionError: If the graph has a function with that ID already.
166 :raises ValueError: If parameter 'kind' is None.
167 :raises TypeError: If parameter 'kind' is not of type :class:`FunctionKind`.
168 :raises TypeError: If parameter 'controlFlowGraph' is not of type
169 :class:`~pyTooling.ControlFlow.ControlFlowGraph`.
170 :raises ValueError: If parameter 'controlFlowGraph' is given for a function of kind
171 :attr:`FunctionKind.Unknown`.
172 """
173 super().__init__(count, value, keyValuePairs)
175 if callGraph is None:
176 raise ValueError("Parameter 'callGraph' is None.")
177 elif not isinstance(callGraph, _CallGraph):
178 ex = TypeError("Parameter 'callGraph' is not of type 'CallGraph'.")
179 ex.add_note(f"Got type '{getFullyQualifiedName(callGraph)}'.")
180 raise ex
182 if functionID is None:
183 raise ValueError("Parameter 'functionID' is None.")
184 elif not isinstance(functionID, Hashable):
185 ex = TypeError("Parameter 'functionID' is not hashable.")
186 ex.add_note(f"Got type '{getFullyQualifiedName(functionID)}'.")
187 raise ex
188 elif functionID in callGraph._functions:
189 raise DuplicateFunctionError(f"Call graph '{callGraph._name}' has a function '{functionID}' already.")
191 if kind is None:
192 raise ValueError("Parameter 'kind' is None.")
193 elif not isinstance(kind, FunctionKind):
194 ex = TypeError("Parameter 'kind' is not of type 'FunctionKind'.")
195 ex.add_note(f"Got type '{getFullyQualifiedName(kind)}'.")
196 raise ex
198 if controlFlowGraph is not None:
199 if not isinstance(controlFlowGraph, _ControlFlowGraph):
200 ex = TypeError("Parameter 'controlFlowGraph' is not of type 'ControlFlowGraph'.")
201 ex.add_note(f"Got type '{getFullyQualifiedName(controlFlowGraph)}'.")
202 raise ex
203 elif kind is FunctionKind.Unknown:
204 ex = ValueError("Parameter 'controlFlowGraph' is given for a function of kind 'Unknown'.")
205 ex.add_note(f"Got function '{functionID}'.")
206 raise ex
208 self._callGraph = callGraph
209 self._id = functionID
210 self._kind = kind
211 self._controlFlowGraph = controlFlowGraph
212 self._inboundCalls = []
213 self._outboundCalls = []
215 callGraph._functions[functionID] = self
217 @readonly
218 def CallGraph(self) -> _CallGraph:
219 """
220 Read-only property to access the call graph this function belongs to (:attr:`_callGraph`).
222 :returns: The call graph.
223 """
224 return self._callGraph
226 @readonly
227 def ID(self) -> Hashable:
228 """
229 Read-only property to access the function's ID (:attr:`_id`).
231 :returns: ID of the function, unique within its graph.
232 """
233 return self._id
235 @readonly
236 def Kind(self) -> FunctionKind:
237 """
238 Read-only property to access the function's kind (:attr:`_kind`).
240 :returns: Kind of the function.
241 """
242 return self._kind
244 @readonly
245 def ControlFlowGraph(self) -> Nullable[_ControlFlowGraph]:
246 """
247 Read-only property to access the function's control flow graph (:attr:`_controlFlowGraph`).
249 :returns: The control flow graph, or ``None`` if it's unknown.
250 """
251 return self._controlFlowGraph
253 @readonly
254 def InboundCalls(self) -> tuple[Call, ...]:
255 """
256 Read-only property to return the calls of this function.
258 :returns: The inbound calls, in the order they were created.
259 """
260 return tuple(self._inboundCalls)
262 @readonly
263 def OutboundCalls(self) -> tuple[Call, ...]:
264 """
265 Read-only property to return the calls this function makes.
267 :returns: The outbound calls, in the order they were created.
268 """
269 return tuple(self._outboundCalls)
271 def CallTo(
272 self,
273 callee: Function,
274 callCount: Nullable[int] = None,
275 callValue: Nullable[Any] = None,
276 callKind: CallKind = CallKind.Default,
277 callSite: Nullable[BasicBlock] = None,
278 keyValuePairs: Nullable[Mapping[Hashable, Any]] = None
279 ) -> Call:
280 """
281 Create an outbound call from this function to the called function.
283 :param callee: The called function.
284 :param callCount: Optional, how often the call was made. Default: ``None``.
285 :param callValue: Optional, any value attached to the call, e.g. the call's source line. Default: ``None``.
286 :param callKind: Optional, kind of the call. Default: :attr:`CallKind.Default`.
287 :param callSite: Optional, the block of the caller's control flow graph the call is made from.
288 Default: ``None``.
289 :param keyValuePairs: Optional, mapping (dictionary) of key-value-pairs for the call. Default: ``None``.
290 :returns: The call from this function to the callee.
291 :raises ValueError: If parameter 'callee' is None.
292 :raises TypeError: If parameter 'callee' is not of type :class:`Function`.
293 :raises ValueError: If parameter 'callee' belongs to another graph than this function.
294 :raises TypeError: If parameter 'callCount' is not of type :class:`int`.
295 :raises ValueError: If parameter 'callCount' is negative.
296 :raises ValueError: If parameter 'callKind' is None.
297 :raises TypeError: If parameter 'callKind' is not of type :class:`CallKind`.
298 :raises TypeError: If parameter 'callSite' is not of type :class:`~pyTooling.ControlFlow.BasicBlock`.
299 :raises ValueError: If parameter 'callSite' is given, but the caller has no control flow graph.
300 :raises ValueError: If parameter 'callSite' belongs to another control flow graph than the caller's.
301 :raises ValueError: If parameter 'callSite' is not a block of :attr:`~pyTooling.ControlFlow.BlockKind.Code`.
303 .. seealso::
305 :meth:`CallFrom`
306 |rarr| Create an inbound call from a calling function to this function.
307 """
308 return Call(
309 self, callee, callKind, callSite=callSite, count=callCount, value=callValue, keyValuePairs=keyValuePairs
310 )
312 def CallFrom(
313 self,
314 caller: Function,
315 callCount: Nullable[int] = None,
316 callValue: Nullable[Any] = None,
317 callKind: CallKind = CallKind.Default,
318 callSite: Nullable[BasicBlock] = None,
319 keyValuePairs: Nullable[Mapping[Hashable, Any]] = None
320 ) -> Call:
321 """
322 Create an inbound call from the calling function to this function.
324 :param caller: The calling function.
325 :param callCount: Optional, how often the call was made. Default: ``None``.
326 :param callValue: Optional, any value attached to the call, e.g. the call's source line. Default: ``None``.
327 :param callKind: Optional, kind of the call. Default: :attr:`CallKind.Default`.
328 :param callSite: Optional, the block of the caller's control flow graph the call is made from.
329 Default: ``None``.
330 :param keyValuePairs: Optional, mapping (dictionary) of key-value-pairs for the call. Default: ``None``.
331 :returns: The call from the caller to this function.
332 :raises ValueError: If parameter 'caller' is None.
333 :raises TypeError: If parameter 'caller' is not of type :class:`Function`.
334 :raises ValueError: If this function belongs to another graph than parameter 'caller'.
335 :raises TypeError: If parameter 'callCount' is not of type :class:`int`.
336 :raises ValueError: If parameter 'callCount' is negative.
337 :raises ValueError: If parameter 'callKind' is None.
338 :raises TypeError: If parameter 'callKind' is not of type :class:`CallKind`.
339 :raises TypeError: If parameter 'callSite' is not of type :class:`~pyTooling.ControlFlow.BasicBlock`.
340 :raises ValueError: If parameter 'callSite' is given, but the caller has no control flow graph.
341 :raises ValueError: If parameter 'callSite' belongs to another control flow graph than the caller's.
342 :raises ValueError: If parameter 'callSite' is not a block of :attr:`~pyTooling.ControlFlow.BlockKind.Code`.
344 .. seealso::
346 :meth:`CallTo`
347 |rarr| Create an outbound call from this function to a called function.
348 """
349 return Call(
350 caller, self, callKind, callSite=callSite, count=callCount, value=callValue, keyValuePairs=keyValuePairs
351 )
354@export
355class Call(BaseWithCountAndValue):
356 """
357 A call in a call graph, leading from the calling function to the called function.
359 The call's :class:`CallKind` says how the callee is determined. A function calling itself is a call from and to the
360 same function. Two functions can be connected by several calls, e.g. one per call site.
361 """
362 _caller: Function #: The calling function.
363 _callee: Function #: The called function.
364 _kind: CallKind #: Kind of the call.
365 _callSite: Nullable[BasicBlock] #: The block of the caller's control flow graph the call is made from, if it's known.
367 def __init__(
368 self,
369 caller: Function,
370 callee: Function,
371 kind: CallKind = CallKind.Default,
372 *,
373 callSite: Nullable[BasicBlock] = None,
374 count: Nullable[int] = None,
375 value: Nullable[Any] = None,
376 keyValuePairs: Nullable[Mapping[Hashable, Any]] = None
377 ) -> None:
378 """
379 Initializes a call between two functions of a graph and adds it to the graph.
381 :param caller: The calling function.
382 :param callee: The called function.
383 :param kind: Optional, kind of the call. Default: :attr:`CallKind.Default`.
384 :param callSite: Optional, the block of the caller's control flow graph the call is made from.
385 Default: ``None``.
386 :param count: Optional, how often the call was made. Default: ``None``.
387 :param value: Optional, any value attached to the call, e.g. the source line of the call. Default: ``None``.
388 :param keyValuePairs: Optional, mapping (dictionary) of key-value-pairs. Default: ``None``.
389 :raises TypeError: If parameter 'count' is not of type :class:`int`.
390 :raises ValueError: If parameter 'count' is negative.
391 :raises ValueError: If parameter 'caller' is None.
392 :raises TypeError: If parameter 'caller' is not of type :class:`Function`.
393 :raises ValueError: If parameter 'callee' is None.
394 :raises TypeError: If parameter 'callee' is not of type :class:`Function`.
395 :raises ValueError: If parameter 'callee' belongs to another graph than parameter 'caller'.
396 :raises ValueError: If parameter 'kind' is None.
397 :raises TypeError: If parameter 'kind' is not of type :class:`CallKind`.
398 :raises TypeError: If parameter 'callSite' is not of type :class:`~pyTooling.ControlFlow.BasicBlock`.
399 :raises ValueError: If parameter 'callSite' is given, but the caller has no control flow graph.
400 :raises ValueError: If parameter 'callSite' belongs to another control flow graph than the caller's.
401 :raises ValueError: If parameter 'callSite' is not a block of :attr:`~pyTooling.ControlFlow.BlockKind.Code`.
402 """
403 super().__init__(count, value, keyValuePairs)
405 if caller is None:
406 raise ValueError("Parameter 'caller' is None.")
407 elif not isinstance(caller, Function):
408 ex = TypeError("Parameter 'caller' is not of type 'Function'.")
409 ex.add_note(f"Got type '{getFullyQualifiedName(caller)}'.")
410 raise ex
412 if callee is None:
413 raise ValueError("Parameter 'callee' is None.")
414 elif not isinstance(callee, Function):
415 ex = TypeError("Parameter 'callee' is not of type 'Function'.")
416 ex.add_note(f"Got type '{getFullyQualifiedName(callee)}'.")
417 raise ex
418 elif callee._callGraph is not caller._callGraph:
419 ex = ValueError("Parameter 'callee' belongs to another graph than parameter 'caller'.")
420 ex.add_note(f"Got graphs '{caller._callGraph._name}' and '{callee._callGraph._name}'.")
421 raise ex
423 if kind is None:
424 raise ValueError("Parameter 'kind' is None.")
425 elif not isinstance(kind, CallKind):
426 ex = TypeError("Parameter 'kind' is not of type 'CallKind'.")
427 ex.add_note(f"Got type '{getFullyQualifiedName(kind)}'.")
428 raise ex
430 if callSite is not None:
431 if not isinstance(callSite, BasicBlock):
432 ex = TypeError("Parameter 'callSite' is not of type 'BasicBlock'.")
433 ex.add_note(f"Got type '{getFullyQualifiedName(callSite)}'.")
434 raise ex
435 elif caller._controlFlowGraph is None:
436 ex = ValueError("Parameter 'callSite' is given, but the caller has no control flow graph.")
437 ex.add_note(f"Got caller '{caller._id}'.")
438 raise ex
439 elif callSite._controlFlowGraph is not caller._controlFlowGraph:
440 ex = ValueError("Parameter 'callSite' belongs to another control flow graph than the caller's.")
441 ex.add_note(f"Got graphs '{callSite._controlFlowGraph._name}' and '{caller._controlFlowGraph._name}'.")
442 raise ex
443 elif callSite._kind is not BlockKind.Code:
444 ex = ValueError("Parameter 'callSite' is not a block of code.")
445 ex.add_note(f"Got block '{callSite._id}' of kind '{callSite._kind}'.")
446 raise ex
448 self._caller = caller
449 self._callee = callee
450 self._kind = kind
451 self._callSite = callSite
453 caller._outboundCalls.append(self)
454 callee._inboundCalls.append(self)
455 caller._callGraph._calls.append(self)
457 @readonly
458 def Caller(self) -> Function:
459 """
460 Read-only property to access the calling function (:attr:`_caller`).
462 :returns: The caller.
463 """
464 return self._caller
466 @readonly
467 def Callee(self) -> Function:
468 """
469 Read-only property to access the called function (:attr:`_callee`).
471 :returns: The callee.
472 """
473 return self._callee
475 @readonly
476 def Kind(self) -> CallKind:
477 """
478 Read-only property to access the call's kind (:attr:`_kind`).
480 :returns: Kind of the call.
481 """
482 return self._kind
484 @readonly
485 def CallSite(self) -> Nullable[BasicBlock]:
486 """
487 Read-only property to access the block of the caller's control flow graph the call is made from (:attr:`_callSite`).
489 :returns: The call site, or ``None`` if it's unknown.
490 """
491 return self._callSite
494@export
495class CallGraph(Base):
496 """
497 The call graph of a program: its functions and the calls between them.
499 A function is looked up by its ID: :pycode:`graph.GetFunction("main")` returns the function with ID ``main``, and
500 :pycode:`graph.ContainsFunction("main")` checks if the graph has one. :meth:`IterateFunctions` yields the functions in
501 the order they were created. The graph's dunder methods serve its key-value-pairs, like a function's or a call's.
502 """
503 _name: str #: Name of the graph, e.g. of the program or the translation unit.
504 _functions: dict[Hashable, Function] #: Functions of the graph by ID, in the order they were created.
505 _calls: list[Call] #: Calls of the graph, in the order they were created.
507 def __init__(self, name: str, *, keyValuePairs: Nullable[Mapping[Hashable, Any]] = None) -> None:
508 """
509 Initializes an empty call graph.
511 :param name: Name of the graph, e.g. of the program or the translation unit.
512 :param keyValuePairs: Optional, mapping (dictionary) of key-value-pairs. Default: ``None``.
513 :raises ValueError: If parameter 'name' is None.
514 :raises TypeError: If parameter 'name' is not of type :class:`str`.
515 :raises ValueError: If parameter 'name' is empty.
516 """
517 super().__init__(keyValuePairs)
519 if name is None:
520 raise ValueError("Parameter 'name' is None.")
521 elif not isinstance(name, str):
522 ex = TypeError("Parameter 'name' is not of type 'str'.")
523 ex.add_note(f"Got type '{getFullyQualifiedName(name)}'.")
524 raise ex
525 elif name == "":
526 raise ValueError("Parameter 'name' is empty.")
528 self._name = name
529 self._functions = {}
530 self._calls = []
532 @readonly
533 def Name(self) -> str:
534 """
535 Read-only property to access the name of the graph (:attr:`_name`).
537 :returns: Name of the graph.
538 """
539 return self._name
541 @readonly
542 def Functions(self) -> dict[Hashable, Function]:
543 """
544 Read-only property to access the functions of the graph by ID (:attr:`_functions`).
546 :returns: The functions, in the order they were created.
547 """
548 return self._functions
550 @readonly
551 def Calls(self) -> list[Call]:
552 """
553 Read-only property to access the calls of the graph (:attr:`_calls`).
555 :returns: The calls, in the order they were created.
556 """
557 return self._calls
559 @readonly
560 def FunctionCount(self) -> int:
561 """
562 Read-only property to return the number of functions in the graph.
564 :returns: Number of functions.
565 """
566 return len(self._functions)
568 def ContainsFunction(self, functionID: Hashable) -> bool:
569 """
570 Check if the graph has a function with the given ID.
572 :param functionID: ID of the function.
573 :returns: ``True``, if the graph has a function with that ID.
574 """
575 return functionID in self._functions
577 def GetFunction(self, functionID: Hashable) -> Function:
578 """
579 Return the function with the given ID.
581 :param functionID: ID of the function.
582 :returns: The function with that ID.
583 :raises KeyError: If the graph has no function with that ID.
584 """
585 return self._functions[functionID]
587 def IterateFunctions(
588 self,
589 predicate: Nullable[Callable[[Function], bool]] = None
590 ) -> Generator[Function, None, None]:
591 """
592 Iterate all or selected functions of the graph.
594 If parameter ``predicate`` is not None, the given filter function is used to skip functions in the generator.
596 :param predicate: Optional, filter function accepting any function and returning a boolean.
597 :returns: A generator to iterate the functions, in the order they were created.
598 """
599 if predicate is None:
600 yield from self._functions.values()
601 else:
602 for function in self._functions.values():
603 if predicate(function):
604 yield function
606 def ToGraph(self) -> Graph:
607 """
608 Convert the call graph into a :class:`~pyTooling.Graph.Graph`.
610 Every function becomes a :class:`~pyTooling.Graph.Vertex` with the function's ID as
611 :attr:`~pyTooling.Graph.Vertex.ID` and the function's value as :attr:`~pyTooling.Graph.Vertex.Value`, so
612 :meth:`Graph.GetVertexByID <pyTooling.Graph.Graph.GetVertexByID>` finds a function's vertex. Every call becomes an
613 :class:`~pyTooling.Graph.Edge` from the caller to the callee, with the call's kind as
614 :attr:`~pyTooling.Graph.BaseEdge.Kind` and the call's value as value.
616 The graph, its vertices and its edges carry a copy of the key-value-pairs of the call graph, its functions and its
617 calls. Vertices and edges carry these key-value-pairs more, which a writer like :mod:`pyTooling.Graph.GraphML`
618 writes out:
620 * ``kind``: the function's :class:`FunctionKind` or the call's :class:`CallKind` member, whose :func:`str` is its
621 name.
622 * ``count``: the execution count; only if it's known.
623 * ``callsite``: on an edge, the ID of the call site's block; only if the call site is known.
625 :returns: The graph, named like the call graph.
626 """
627 graph = Graph(name=self._name, keyValuePairs=self._dict)
629 vertices: dict[Hashable, Vertex] = {}
630 for function in self._functions.values():
631 keyValuePairs: dict[Hashable, Any] = {**function._dict, "kind": function._kind}
632 if function._count is not None:
633 keyValuePairs["count"] = function._count
635 vertices[function._id] = Vertex(
636 vertexID=function._id,
637 value=function._value,
638 keyValuePairs=keyValuePairs,
639 graph=graph
640 )
642 for call in self._calls:
643 keyValuePairs = {**call._dict, "kind": call._kind}
644 if call._count is not None:
645 keyValuePairs["count"] = call._count
647 if call._callSite is not None:
648 keyValuePairs["callsite"] = call._callSite._id
650 vertices[call._caller._id].EdgeToVertex(
651 vertices[call._callee._id],
652 edgeValue=call._value,
653 edgeKind=call._kind,
654 keyValuePairs=keyValuePairs
655 )
657 return graph
660# ``Function`` has a property named like the class ``CallGraph``, so it annotates its field with this alias.
661_CallGraph = CallGraph