you can use windbg wt (watch and trace function to generate an execution flow)
the demo below is for the code in your query compiled in debug mode (to ensure function calls exist and not optimised away by a simple substitution)
code used for example compiled in msvcpp2ktenexp debug
int glob = 0;
void f1(int a) { printf("%d ",a); glob++;}
void f3(int a) { printf("F3 %d ",a);}
void f5(int a) { printf("F5 %d ",a);}
void f6(int a) { printf("%d ",a);}
void f4(int a) { f5(5);}
void f2(int a) { if(glob == 8) { f3(3); f4(4); } else { f6(6); } }
void main() { rndrob:f1(1);f2(2);f1(1);f2(2);if(glob<10){goto rndrob;} }
dry run results
>flowt.exe
1 6 1 6 1 6 1 6 1 6 1 6 1 6 1 F3 3 F5 5 1 6 1 6
windbg wt results for module flowt function main() edited to remove fluff statistics at the end shows printf() was called 21 times f3() f4() once etc etc
:>cdb -c "g main;wt -m flowt" flowt.exe
Tracing flowt!main to return address 00411bcf
59 0 [ 0] flowt!main
62 0 [ 1] flowt!f1
1 72 0 [ 2] MSVCR100D!printf
65 72 [ 1] flowt!f1
62 156 [ 0] flowt!main
61 0 [ 1] flowt!f2
62 0 [ 2] flowt!f6
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
61 0 [ 1] flowt!f2
62 0 [ 2] flowt!f3
F3 3 72 0 [ 3] MSVCR100D!printf
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
64 153 [ 1] flowt!f2
62 0 [ 3] flowt!f5
F5 5 72 0 [ 4] MSVCR100D!printf
65 72 [ 3] flowt!f5
141 4073 [ 0] flowt!main
4214 instructions were executed in 4213 events (0 from other threads)
Function Name Invocations MinInst MaxInst AvgInst
MSVCR100D!printf 21 72 72 72
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
flowt!f1 10 77 77 77
flowt!f2 10 71 75 71
flowt!f3 1 74 74 74
flowt!f4 1 69 69 69
flowt!f5 1 74 74 74
flowt!f6 9 74 74 74
flowt!main 1 141 141 141