00001 /** @file rutz/prof.cc class for accumulating profiling information */ 00002 00003 /////////////////////////////////////////////////////////////////////// 00004 // 00005 // Copyright (c) 1999-2004 California Institute of Technology 00006 // Copyright (c) 2004-2007 University of Southern California 00007 // Rob Peters <rjpeters at klab dot caltech dot edu> 00008 // 00009 // created: Thu Jun 30 14:47:13 2005 00010 // commit: $Id: prof.cc 8249 2007-04-12 06:03:40Z rjpeters $ 00011 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/rutz/prof.cc $ 00012 // 00013 // -------------------------------------------------------------------- 00014 // 00015 // This file is part of GroovX. 00016 // [http://www.klab.caltech.edu/rjpeters/groovx/] 00017 // 00018 // GroovX is free software; you can redistribute it and/or modify it 00019 // under the terms of the GNU General Public License as published by 00020 // the Free Software Foundation; either version 2 of the License, or 00021 // (at your option) any later version. 00022 // 00023 // GroovX is distributed in the hope that it will be useful, but 00024 // WITHOUT ANY WARRANTY; without even the implied warranty of 00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00026 // General Public License for more details. 00027 // 00028 // You should have received a copy of the GNU General Public License 00029 // along with GroovX; if not, write to the Free Software Foundation, 00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 00031 // 00032 /////////////////////////////////////////////////////////////////////// 00033 00034 #ifndef GROOVX_RUTZ_PROF_CC_UTC20050630214713_DEFINED 00035 #define GROOVX_RUTZ_PROF_CC_UTC20050630214713_DEFINED 00036 00037 #include "rutz/prof.h" 00038 00039 #include "rutz/abort.h" 00040 #include "rutz/mutex.h" 00041 #include "rutz/staticstack.h" 00042 00043 #include <algorithm> // for std::stable_sort() 00044 #include <cstdio> 00045 #include <functional> 00046 #include <iomanip> 00047 #include <new> // for std::nothrow 00048 #include <ostream> 00049 #include <pthread.h> 00050 #include <string> 00051 00052 namespace 00053 { 00054 // 00055 // data and thread info for the profile output file 00056 // 00057 00058 bool g_pdata_print_at_exit = false; 00059 std::string g_pdata_fname = "prof.out"; 00060 FILE* g_pdata_file = 0; 00061 pthread_once_t g_pdata_file_once = PTHREAD_ONCE_INIT; 00062 pthread_mutex_t g_pdata_mutex = PTHREAD_MUTEX_INITIALIZER; 00063 00064 void open_pdata_file() 00065 { 00066 if (g_pdata_fname.length() > 0) 00067 g_pdata_file = fopen(g_pdata_fname.c_str(), "w"); 00068 00069 if (g_pdata_file == 0) 00070 { 00071 fprintf(stderr, 00072 "couldn't open profile file '%s' for writing\n", 00073 g_pdata_fname.c_str()); 00074 } 00075 } 00076 00077 // 00078 // data and thread info for a global list of all rutz::prof objects 00079 // 00080 00081 typedef rutz::static_stack<rutz::prof*, 2048> prof_list; 00082 00083 prof_list* g_prof_list = 0; 00084 pthread_once_t g_prof_list_once = PTHREAD_ONCE_INIT; 00085 pthread_mutex_t g_prof_list_mutex = PTHREAD_MUTEX_INITIALIZER; 00086 00087 void initialize_prof_list() 00088 { 00089 // Q: Why do a dynamic allocation here instead of just having a 00090 // static object? 00091 00092 // A: With a static object, we could potentially run in to trouble 00093 // during program exit, if somebody's destructor called 00094 // backtrace::current() after the local static object's destructor 00095 // (i.e., ~backtrace()) had itself already been run. On the other 00096 // hand, with a dynamically-allocated object, we can just let the 00097 // memory dangle (it's not really a memory "leak" since the amount 00098 // of memory is finite and bounded), so the object will never 00099 // become invalid, even during program shutdown. 00100 00101 g_prof_list = new (std::nothrow) prof_list; 00102 00103 if (g_prof_list == 0) 00104 GVX_ABORT("memory allocation failed"); 00105 } 00106 00107 prof_list& all_profs() throw() 00108 { 00109 pthread_once(&g_prof_list_once, &initialize_prof_list); 00110 00111 return *g_prof_list; 00112 } 00113 00114 // 00115 // data and thread info for a global start time 00116 // 00117 00118 rutz::time g_start; 00119 pthread_once_t g_start_once = PTHREAD_ONCE_INIT; 00120 00121 void initialize_start_time() 00122 { 00123 g_start = rutz::prof::get_now_time(rutz::prof::get_timing_mode()); 00124 } 00125 } 00126 00127 /////////////////////////////////////////////////////////////////////// 00128 // 00129 // rutz::prof member definitions 00130 // 00131 /////////////////////////////////////////////////////////////////////// 00132 00133 rutz::prof::timing_mode rutz::prof::s_timing_mode = rutz::prof::RUSAGE; 00134 00135 rutz::prof::prof(const char* s, const char* fname, int lineno) throw(): 00136 m_context_name(s), 00137 m_src_file_name(fname), 00138 m_src_line_no(lineno) 00139 { 00140 reset(); 00141 00142 { 00143 GVX_MUTEX_LOCK(&g_prof_list_mutex); 00144 all_profs().push(this); 00145 } 00146 00147 pthread_once(&g_start_once, &initialize_start_time); 00148 } 00149 00150 rutz::prof::~prof() throw() 00151 { 00152 if (g_pdata_print_at_exit) 00153 { 00154 pthread_once(&g_pdata_file_once, &open_pdata_file); 00155 00156 GVX_MUTEX_LOCK(&g_pdata_mutex); 00157 00158 if (g_pdata_file != 0) 00159 { 00160 print_prof_data(g_pdata_file); 00161 } 00162 } 00163 } 00164 00165 void rutz::prof::reset() throw() 00166 { 00167 m_call_count = 0; 00168 m_total_time.reset(); 00169 m_children_time.reset(); 00170 } 00171 00172 unsigned int rutz::prof::count() const throw() 00173 { 00174 return m_call_count; 00175 } 00176 00177 void rutz::prof::add_time(const rutz::time& t) throw() 00178 { 00179 m_total_time += t; 00180 ++m_call_count; 00181 } 00182 00183 void rutz::prof::add_child_time(const rutz::time& t) throw() 00184 { 00185 m_children_time += t; 00186 } 00187 00188 const char* rutz::prof::context_name() const throw() 00189 { 00190 return m_context_name; 00191 } 00192 00193 const char* rutz::prof::src_file_name() const throw() 00194 { 00195 return m_src_file_name; 00196 } 00197 00198 int rutz::prof::src_line_no() const throw() 00199 { 00200 return m_src_line_no; 00201 } 00202 00203 double rutz::prof::total_time() const throw() 00204 { 00205 return (m_call_count > 0) 00206 ? m_total_time.usec() 00207 : 0.0; 00208 } 00209 00210 double rutz::prof::self_time() const throw() 00211 { 00212 return (m_call_count > 0) 00213 ? m_total_time.usec() - m_children_time.usec() 00214 : 0.0; 00215 } 00216 00217 double rutz::prof::avg_self_time() const throw() 00218 { 00219 return m_call_count > 0 ? (total_time() / m_call_count) : 0.0; 00220 } 00221 00222 void rutz::prof::print_prof_data(FILE* file) const throw() 00223 { 00224 if (file == 0) 00225 GVX_ABORT("FILE* was null"); 00226 00227 const double total_elapsed_usec = 00228 (rutz::prof::get_now_time(s_timing_mode) - g_start).usec(); 00229 00230 // Don't try to convert the double values to long or int, because 00231 // we're likely to overflow and potentially cause a floating-point 00232 // exception. 00233 fprintf(file, "%10.0f %6u %10.0f %4.1f%% %10.0f %4.1f%% %s\n", 00234 avg_self_time(), count(), 00235 self_time(), (100.0 * self_time()) / total_elapsed_usec, 00236 total_time(), (100.0 * total_time()) / total_elapsed_usec, 00237 m_context_name); 00238 } 00239 00240 void rutz::prof::print_prof_data(std::ostream& os) const throw() 00241 { 00242 os.exceptions(std::ios::goodbit); 00243 00244 const double total_elapsed_usec = 00245 (rutz::prof::get_now_time(s_timing_mode) - g_start).usec(); 00246 00247 // Don't try to convert the double values to long or int, because 00248 // we're likely to overflow and potentially cause a floating-point 00249 // exception. 00250 os << std::fixed 00251 << std::setw(10) << std::setprecision(0) << avg_self_time() << ' ' 00252 << std::setw(6) << count() << ' ' 00253 << std::setw(10) << std::setprecision(0) << self_time() << ' ' 00254 << std::setw(4) << std::setprecision(1) 00255 << (100.0 * self_time()) / total_elapsed_usec << "% " 00256 << std::setw(10) << std::setprecision(0) << total_time() << ' ' 00257 << std::setw(4) << std::setprecision(1) 00258 << (100.0 * total_time()) / total_elapsed_usec << "% " 00259 << m_context_name << '\n'; 00260 } 00261 00262 void rutz::prof::print_at_exit(bool yes_or_no) throw() 00263 { 00264 g_pdata_print_at_exit = yes_or_no; 00265 } 00266 00267 void rutz::prof::prof_summary_file_name(const char* fname) 00268 { 00269 GVX_MUTEX_LOCK(&g_pdata_mutex); 00270 00271 if (fname == 0) 00272 fname = ""; 00273 00274 g_pdata_fname = fname; 00275 } 00276 00277 void rutz::prof::reset_all_prof_data() throw() 00278 { 00279 GVX_MUTEX_LOCK(&g_prof_list_mutex); 00280 std::for_each(all_profs().begin(), all_profs().end(), 00281 std::mem_fun(&rutz::prof::reset)); 00282 } 00283 00284 namespace 00285 { 00286 // comparison function for use with std::stable_sort() 00287 bool compare_total_time(rutz::prof* p1, rutz::prof* p2) throw() 00288 { 00289 return p1->total_time() < p2->total_time(); 00290 } 00291 } 00292 00293 void rutz::prof::print_all_prof_data(FILE* file) throw() 00294 { 00295 GVX_MUTEX_LOCK(&g_prof_list_mutex); 00296 std::stable_sort(all_profs().begin(), all_profs().end(), 00297 compare_total_time); 00298 00299 for (unsigned int i = 0; i < all_profs().size(); ++i) 00300 { 00301 if (all_profs()[i]->count() > 0) 00302 all_profs()[i]->print_prof_data(file); 00303 } 00304 } 00305 00306 void rutz::prof::print_all_prof_data(std::ostream& os) throw() 00307 { 00308 GVX_MUTEX_LOCK(&g_prof_list_mutex); 00309 std::stable_sort(all_profs().begin(), all_profs().end(), 00310 compare_total_time); 00311 00312 for (unsigned int i = 0; i < all_profs().size(); ++i) 00313 { 00314 if (all_profs()[i]->count() > 0) 00315 all_profs()[i]->print_prof_data(os); 00316 } 00317 } 00318 00319 static const char __attribute__((used)) vcid_groovx_rutz_prof_cc_utc20050630214713[] = "$Id: prof.cc 8249 2007-04-12 06:03:40Z rjpeters $ $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/rutz/prof.cc $"; 00320 #endif // !GROOVX_RUTZ_PROF_CC_UTC20050630214713DEFINED