Yeah, it's called at the end of every complete path, but there are less paths than you'd expect, because the return value is irrelevant and gets discarded (since we'd never see the caller during analysis), and then paths get deduplicated based on that. So it's not the correct way to enumerate possible function exits - it's merely a notification that the analysis is exiting a function right now. For the same reason, all other path event callbacks such as PreStmt would not be called separately for every path that reaches that event, but only for the particular node on which the event happens.
You may see how the ExplodedGraph changes when you add a caller, i.e.:
$ cat -n test.c
    1   int foo(int a, int b) {
    2    if (a && b)
    3      return a + b;
    4    if (b)
    5      return b;
    6    return 0;
    7   }
    8
    9   int bar(int a, int b) {
   10    return foo(a, b);
   11   }
$ clang -cc1 -analyze -analyzer-display-progress -analyzer-checker core,debug.DumpTraversal test.c
ANALYZE (Syntax): test.c foo
ANALYZE (Syntax): test.c bar
ANALYZE (Path, Inline_Regular): test.c bar
--BEGIN FUNCTION--
2 BinaryOperator
4 IfStmt
--END FUNCTION--
2 BinaryOperator
4 IfStmt
--END FUNCTION--
(see -analyzer-viz-egraph-graphviz to visualize the actual exploded graph)
There may also be incomplete paths, which were terminated before reaching the end of the function (eg. maximum exploded graph size exceeded), and there may also be exits from the function that were never reached during analysis (because all of their respective paths were terminated before that happened).
Also your question looks suspicious to me in the sense that you might be trying to do something that's either not going to work or can be done a lot easier.
Also you might want to have a look at checkEndAnalysis which is called only once per analysis and provides the fully constructed ExplodedGraph to traverse, explore, or gather whatever statistics you want.