Boost logo

Boost-Commit :

From: steven_at_[hidden]
Date: 2008-08-13 17:41:22


Author: steven_watanabe
Date: 2008-08-13 17:41:22 EDT (Wed, 13 Aug 2008)
New Revision: 48132
URL: http://svn.boost.org/trac/boost/changeset/48132

Log:
Add primitive call graph generation--only msvc works
Text files modified:
   sandbox/tools/profile_templates/src/filter.cpp | 90 ++++++++++++++++++++++++++++++
   sandbox/tools/profile_templates/src/postprocess.cpp | 118 +++++++++++++++++++++++++++++++++++++++
   sandbox/tools/profile_templates/template-profile.jam | 34 ++++++-----
   3 files changed, 224 insertions(+), 18 deletions(-)

Modified: sandbox/tools/profile_templates/src/filter.cpp
==============================================================================
--- sandbox/tools/profile_templates/src/filter.cpp (original)
+++ sandbox/tools/profile_templates/src/filter.cpp 2008-08-13 17:41:22 EDT (Wed, 13 Aug 2008)
@@ -1,9 +1,19 @@
+// filter.cpp
+//
+// Copyright (c) 2008
+// Steven Watanabe
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
 #include <string>
 #include <cstdio>
 
 const char* search("template_profiler");
+const char* back_trace_search("see reference to");
 
-int main() {
+void copy_flat_only() {
     std::string buffer;
     int ch;
     int pos = 0;
@@ -39,3 +49,81 @@
         }
     }
 }
+
+void copy_call_graph() {
+#ifdef _MSC_VER
+ std::string buffer;
+ int ch;
+ int pos = 0;
+ bool matched = false;
+ int counter = 0;
+ while((ch = std::getchar()) != EOF) {
+ buffer.push_back(static_cast<char>(ch));
+ if(ch == '\n') {
+ if(matched) {
+ for(std::size_t i = 0; i < buffer.size(); ++i) {
+ std::putchar(buffer[i]);
+ }
+ if(++counter % 100 == 0) {
+ std::fprintf(stderr, "On Instantiation %d\n", counter);
+ }
+ buffer.clear();
+ matched = false;
+ // process instantiation back-trace
+ pos = 0;
+ while((ch = std::getchar()) != EOF) {
+ if(ch == ' ') {
+ buffer.push_back(static_cast<char>(ch));
+ while((ch = std::getchar()) != EOF) {
+ buffer.push_back(static_cast<char>(ch));
+ if(ch == '\n') {
+ if(matched) {
+ for(std::size_t i = 0; i < buffer.size(); ++i) {
+ std::putchar(buffer[i]);
+ }
+ }
+ buffer.clear();
+ matched = false;
+ pos = 0;
+ break;
+ }
+ if(ch == back_trace_search[pos]) {
+ ++pos;
+ if(back_trace_search[pos] == '\0') {
+ matched = true;
+ }
+ } else {
+ pos = 0;
+ }
+ }
+ } else {
+ std::ungetc(ch, stdin);
+ break;
+ }
+ }
+ }
+ buffer.clear();
+ matched = false;
+ pos = 0;
+ }
+ if(ch == search[pos]) {
+ ++pos;
+ if(search[pos] == '\0') {
+ matched = true;
+ }
+ } else {
+ pos = 0;
+ }
+ }
+#else
+ #error gcc is not supported yet.
+#endif
+}
+
+int main(int argc, char** argv) {
+ if(argc == 2 && std::strcmp(argv[1], "--call-graph") == 0) {
+ copy_call_graph();
+ } else {
+ copy_flat_only();
+ }
+}

Modified: sandbox/tools/profile_templates/src/postprocess.cpp
==============================================================================
--- sandbox/tools/profile_templates/src/postprocess.cpp (original)
+++ sandbox/tools/profile_templates/src/postprocess.cpp 2008-08-13 17:41:22 EDT (Wed, 13 Aug 2008)
@@ -1,8 +1,20 @@
+// postprocess.cpp
+//
+// Copyright (c) 2008
+// Steven Watanabe
+//
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
 #include <boost/regex.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
 #include <string>
 #include <iostream>
 #include <fstream>
 #include <map>
+#include <set>
 #include <exception>
 #include <iterator>
 #include <algorithm>
@@ -11,10 +23,13 @@
 #ifdef _MSC_VER
 
 boost::regex warning_message("(.*) : warning C4150: deletion of pointer to incomplete type 'template_profiler::incomplete'; no destructor called");
+boost::regex call_graph_line(" (.*)\\((\\d+)\\) : see reference to .*");
+boost::regex split_file_and_line("(.*)\\((\\d+)\\)");
 
 #elif defined(__GNUC__)
 
 boost::regex warning_message("(.*): warning: division by zero in .template_profiler::value / 0.");
+#error gcc is not supported yet
 
 #else
 
@@ -40,17 +55,55 @@
     }
 };
 
+typedef std::pair<std::string, int> line_id;
+
+struct node_info {
+ node_info() : count(0), total_with_children(0) {}
+ std::map<const line_id*, int> children;
+ std::map<const line_id*, int> parents;
+ int count;
+ int total_with_children;
+};
+
+struct call_graph_less {
+ template<class T>
+ bool operator()(const T& lhs, const T& rhs) const {
+ return(lhs.second.total_with_children > rhs.second.total_with_children);
+ }
+};
+
+struct print_line_id {
+ print_line_id(const line_id* x) : line(x) {}
+ const line_id* line;
+};
+
+std::ostream& operator<<(std::ostream& os, const print_line_id& arg) {
+ os << arg.line->first << '(' << arg.line->second << ')';
+ return(os);
+}
+
 int main(int argc, char** argv) {
- if(argc != 2) {
+ const char* input_file_name = 0;
+ bool use_call_graph = false;
+ for(int i = 1; i < argc; ++i) {
+ if(std::strcmp(argv[i], "--call-graph") == 0) {
+ use_call_graph = true;
+ } else {
+ input_file_name = argv[i];
+ }
+ }
+ if(input_file_name == 0) {
         std::cerr << "Usage: " << argv[0] << " <input file>\n";
         return(EXIT_FAILURE);
     }
+
+
     std::map<std::string, int> messages;
     std::string line;
     int total_matches = 0;
     std::ptrdiff_t max_match_length = 0;
     {
- std::ifstream input(argv[1]);
+ std::ifstream input(input_file_name);
         while(std::getline(input, line)) {
             boost::smatch match;
             if(boost::regex_match(line, match, warning_message)) {
@@ -68,4 +121,65 @@
     std::cout << std::setfill('-') << std::setw(max_match_length + 20) << '-' << std::setfill(' ') << std::endl;
     print p = { &cummulative, max_match_length };
     std::for_each(copy.begin(), copy.end(), p);
+
+ if(use_call_graph) {
+ std::map<const line_id*, node_info> graph;
+ std::set<line_id> lines;
+ typedef std::pair<std::string, int> raw_line;
+ BOOST_FOREACH(const raw_line& l, messages) {
+ boost::smatch match;
+ boost::regex_match(l.first, match, split_file_and_line);
+ lines.insert(line_id(match[1], boost::lexical_cast<int>(match[2].str())));
+ }
+ const line_id* current_instantiation = 0;
+ std::ifstream input(input_file_name);
+ while(std::getline(input, line)) {
+ boost::smatch match;
+ if(boost::regex_match(line, match, warning_message)) {
+ std::string file_and_line(match[1].str());
+ boost::regex_match(file_and_line, match, split_file_and_line);
+ std::string file = match[1];
+ int line = boost::lexical_cast<int>(match[2].str());
+ current_instantiation = &*lines.find(line_id(file, line));
+ ++graph[current_instantiation].total_with_children;
+ ++graph[current_instantiation].count;
+ } else if(boost::regex_match(line, match, call_graph_line)) {
+ std::string file = match[1];
+ int line = boost::lexical_cast<int>(match[2].str());
+ std::set<line_id>::const_iterator pos = lines.lower_bound(line_id(file, line));
+ if(pos != lines.end()) {
+ if(*pos != line_id(file, line)) {
+ if(pos != lines.begin()) {
+ --pos;
+ }
+ }
+ } else if(pos != lines.begin()) {
+ --pos;
+ } else {
+ break;
+ }
+ const line_id* parent = &*pos;
+ ++graph[current_instantiation].parents[parent];
+ ++graph[parent].children[current_instantiation];
+ ++graph[parent].total_with_children;
+ }
+ }
+ typedef std::pair<const line_id*, node_info> call_graph_node_t;
+ std::vector<call_graph_node_t> call_graph;
+ std::copy(graph.begin(), graph.end(), std::back_inserter(call_graph));
+ std::sort(call_graph.begin(), call_graph.end(), call_graph_less());
+ std::cout << "\nCall Graph\n\n";
+ BOOST_FOREACH(const call_graph_node_t& node, call_graph) {
+ std::cout << print_line_id(node.first) << " (" << node.second.count << ")\n";
+ typedef std::map<const line_id*, int>::const_reference node_t;
+ std::cout << " Parents:\n";
+ BOOST_FOREACH(node_t n, node.second.parents) {
+ std::cout << " " << print_line_id(n.first) << " (" << n.second << ")\n";
+ }
+ std::cout << " Children:\n";
+ BOOST_FOREACH(node_t n, node.second.children) {
+ std::cout << " " << print_line_id(n.first) << " (" << n.second << "/" << graph[n.first].count << ")\n";
+ }
+ }
+ }
 }

Modified: sandbox/tools/profile_templates/template-profile.jam
==============================================================================
--- sandbox/tools/profile_templates/template-profile.jam (original)
+++ sandbox/tools/profile_templates/template-profile.jam 2008-08-13 17:41:22 EDT (Wed, 13 Aug 2008)
@@ -27,41 +27,45 @@
 
 feature.feature <template-profile-toolset> : msvc gcc : free ;
 feature.feature <template-profile-filter> : : path incidental ;
+feature.feature <flat-profile> : on off : incidental propagated ;
+feature.feature <call-graph> : on off : incidental propagated ;
 
-toolset.flags gcc.compile.c++.preprocess INSTRUMENT-ACTION : $(INSTRUMENT-ACTION) : unchecked ;
+rule init-toolset ( toolset ) {
+ toolset.flags $(toolset).compile.c++.preprocess INSTRUMENT-ACTION : $(INSTRUMENT-ACTION) : unchecked ;
+ toolset.flags $(toolset).compile.c++.template-profile FILTER <template-profile-filter> : : unchecked ;
+ toolset.flags $(toolset).compile.c++.template-profile FILTER-OPTIONS <call-graph>on : --call-graph : unchecked ;
+ generators.register-c-compiler $(toolset).compile.c++.preprocess : CPP : INSTRUMENTED_TEMPLATE_PREPROCESSED_CPP : <toolset>$(toolset) ;
+}
 
 module gcc {
 
+ import template-profile : init-toolset ;
+
+ template-profile.init-toolset gcc ;
+
     actions compile.c++.preprocess {
         $(CONFIG_COMMAND) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -I"$(INCLUDES)" -E "$(>)" | $(INSTRUMENT-ACTION) >"$(<)"
     }
 
- generators.register-c-compiler gcc.compile.c++.preprocess : CPP : INSTRUMENTED_TEMPLATE_PREPROCESSED_CPP : <toolset>gcc ;
-
     actions compile.c++.template-profile {
- "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -D PROFILE_TEMPLATES -I"$(INCLUDES)" -c "$(>:W)" 2>&1 | "$(FILTER)" >"$(<)"
+ "$(CONFIG_COMMAND)" $(LANG) -ftemplate-depth-$(TEMPLATE_DEPTH) $(OPTIONS) $(USER_OPTIONS) -D$(DEFINES) -D PROFILE_TEMPLATES -I"$(INCLUDES)" -c "$(>:W)" 2>&1 "$(FILTER)" | $(FILTER-OPTIONS) >"$(<)"
     }
 
- toolset.flags gcc.compile.c++.template-profile FILTER <template-profile-filter> ;
-
 }
 
-toolset.flags msvc.compile.c++.preprocess INSTRUMENT-ACTION : $(INSTRUMENT-ACTION) : unchecked ;
-
 module msvc {
 
+ import template-profile : init-toolset ;
+ template-profile.init-toolset msvc ;
+
     actions compile.c++.preprocess {
         $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -E $(lang-opt) -U$(UNDEFS) $(CFLAGS) $(C++FLAGS) $(OPTIONS) $(nl)-D$(DEFINES) $(nl)"-I$(INCLUDES:W)")" | $(INSTRUMENT-ACTION) >"$(<[1]:W)"
     }
 
- generators.register-c-compiler msvc.compile.c++.preprocess : CPP : INSTRUMENTED_TEMPLATE_PREPROCESSED_CPP : <toolset>msvc ;
-
     actions compile.c++.template-profile {
- $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -c $(lang-opt) -U$(UNDEFS) $(CFLAGS) $(C++FLAGS) $(OPTIONS) -W4 $(nl)-D$(DEFINES) -D PROFILE_TEMPLATES $(nl)"-I$(INCLUDES:W)")" 2>&1 | "$(FILTER)" >"$(<)"
+ $(.CC) @"@($(<[1]:W).rsp:E="$(>[1]:W)" -c $(lang-opt) -U$(UNDEFS) $(CFLAGS) $(C++FLAGS) $(OPTIONS) -W4 $(nl)-D$(DEFINES) -D PROFILE_TEMPLATES $(nl)"-I$(INCLUDES:W)")" 2>&1 | "$(FILTER)" $(FILTER-OPTIONS) >"$(<)"
     }
 
- toolset.flags msvc.compile.c++.template-profile FILTER <template-profile-filter> ;
-
 }
 
 class warn-generator : generator {
@@ -167,8 +171,8 @@
 feature.feature <postprocess-binary> : : path incidental ;
 
 toolset.flags template-profile.process-raw-profile POSTPROCESS-BINARY <postprocess-binary> ;
+toolset.flags template-profile.process-raw-profile POSTPROCESS-OPTIONS <call-graph>on : --call-graph ;
 
 actions process-raw-profile {
- "$(POSTPROCESS-BINARY)" "$(>)" >"$(<)"
- echo true
+ "$(POSTPROCESS-BINARY)" $(POSTPROCESS-OPTIONS) "$(>)" >"$(<)"
 }


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk