stackcollapse-vsprof.pl 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/perl -w
  2. #
  3. # stackcollapse-vsprof.pl
  4. #
  5. # Parses the CSV file containing a call tree from a visual studio profiler and produces an output suitable for flamegraph.pl.
  6. #
  7. # USAGE: perl stackcollapse-vsprof.pl infile > outfile
  8. #
  9. # WORKFLOW:
  10. #
  11. # This example assumes you have visual studio 2015 installed.
  12. #
  13. # 1. Profile C++ your application using visual studio
  14. # 2. On visual studio, choose export the call tree as csv
  15. # 3. Generate a flamegraph: perl stackcollapse-vsprof CallTreeSummary.csv | perl flamegraph.pl > result_vsprof.svg
  16. #
  17. # INPUT EXAMPLE :
  18. #
  19. # Level,Function Name,Inclusive Samples,Exclusive Samples,Inclusive Samples %,Exclusive Samples %,Module Name,
  20. # 1,"main","8,735",0,100.00,0.00,"an_executable.exe",
  21. # 2,"testing::UnitTest::Run","8,735",0,100.00,0.00,"an_executable.exe",
  22. # 3,"boost::trim_end_iter_select<std::iterator<std::val<std::types<char> > >,boost::is_classifiedF>",306,16,3.50,0.18,"an_executable.exe",
  23. #
  24. # OUTPUT EXAMPLE :
  25. #
  26. # main;testing::UnitTest::Run;boost::trim_end_iter_select<std::iterator<std::val<std::types<char>>>,boost::is_classifiedF> 306
  27. use strict;
  28. sub massage_function_names;
  29. sub parse_integer;
  30. sub print_stack_trace;
  31. # data initialization
  32. my @stack = ();
  33. my $line_number = 0;
  34. my $previous_samples = 0;
  35. my $num_args = $#ARGV + 1;
  36. if ($num_args != 1) {
  37. print "$ARGV[0]\n";
  38. print "Usage : stackcollapse-vsprof.pl <in.cvs> > out.txt\n";
  39. exit;
  40. }
  41. my $input_csv_file = $ARGV[0];
  42. my $line_parser_rx = qr{
  43. ^\s*(\d+?), # level in the stack
  44. ("[^"]+" | [^,]+), # function name (beware of spaces)
  45. ("[^"]+" | [^,]+), # number of samples (beware of locale number formatting)
  46. }ox;
  47. open(my $fh, '<', $input_csv_file) or die "Can't read file '$input_csv_file' [$!]\n";
  48. while (my $current_line = <$fh>){
  49. $line_number = $line_number + 1;
  50. # to discard first line which typically contains headers
  51. next if $line_number == 1;
  52. next if $current_line =~ /^\s*$/o;
  53. ($current_line =~ $line_parser_rx) or die "Error in regular expression at line $line_number : $current_line\n";
  54. my $level = int $1;
  55. my $function = massage_function_names($2);
  56. my $samples = parse_integer($3);
  57. my $stack_len = @stack;
  58. #print "[DEBUG] $line_number : $level $function $samples $stack_len\n";
  59. next if not $level;
  60. ($level <= $stack_len + 1) or die "Error in stack at line $line_number : $current_line\n";
  61. if ($level <= $stack_len) {
  62. print_stack_trace(\@stack, $previous_samples);
  63. my $to_remove = $level - $stack_len - 1;
  64. splice(@stack, $to_remove);
  65. }
  66. $stack_len < 1000 or die "Stack overflow at line $line_number";
  67. push(@stack, $function);
  68. $previous_samples = $samples;
  69. }
  70. print_stack_trace(\@stack, $previous_samples);
  71. sub massage_function_names {
  72. return ($_[0] =~ s/\s*|^"|"$//gro);
  73. }
  74. sub parse_integer {
  75. return int ($_[0] =~ s/[., ]|^"|"$//gro);
  76. }
  77. sub print_stack_trace {
  78. my ($stack_ref, $sample) = @_;
  79. my $stack_trace = join(";", @$stack_ref);
  80. print "$stack_trace $sample\n";
  81. }