├── .travis.yml ├── LICENSE ├── README.md ├── demo.cc ├── dollar.cpp └── dollar.hpp /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | 4 | compiler: 5 | - clang 6 | - gcc 7 | 8 | install: 9 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.pre.sh | bash -x 10 | 11 | script: 12 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.build.sh | bash -x 13 | - wget --quiet -O - https://raw.githubusercontent.com/r-lyeh/depot/master/travis.run.sh | bash -x 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015,2016 r-lyeh (https://github.com/r-lyeh) 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dollar :moneybag: 2 | 3 | - [x] Generic instrumented CPU profiler (C++11) 4 | - [x] Ascii results in plain text, Markdown, TSV and CSV formats. 5 | - [x] Compatible JSON traces for Chrome's built-in trace viewer (chrome://tracing). 6 | - [x] Tiny, self-contained, cross-platform, header-only. 7 | - [x] ZLIB/libPNG licensed. 8 | 9 | ## Quick tutorial (tl;dr) 10 | ```c++ 11 | #include "dollar.hpp" // dollar is enabled by default. compile with -D$= to disable any profiling 12 | int main() { $ // <-- put a dollar after every curly brace to determinate cpu cost of the scope 13 | for( int x = 0; x < 10000000; ++x ) { $ // <-- functions or loops will apply too 14 | // slow stuff... 15 | } 16 | dollar::text(std::cout); // report stats to std::cout in text format; see also csv(), tsv() and markdown() 17 | dollar::chrome(std::ofstream("chrome.json")); // write tracing results to a json file (for chrome://tracing embedded profiler) 18 | dollar::clear(); // clear all scopes (like when entering a new frame) 19 | } 20 | ``` 21 | 22 | ## API 23 | - Determinate the CPU cost of any scope by putting a `$` dollar sign in front of it. 24 | - Or just insert an `dollar raii("name")` object. 25 | - The predefined `$` macro just adds function name, line and number to the RAII object name. 26 | - Use `dollar::text(ostream)`, `dollar::csv(ostream)`, `dollar::tsv(ostream)` or `dollar::markdown(ostream)` to print a text report in any ostream object (like `std::cout`). 27 | - Additionally, use `dollar::chrome(ostream)` to write a chrome://tracing friendly json trace. 28 | - Finally, call `dollar::clear()` when entering a new frame. 29 | 30 | ## API: auto format tool 31 | - @snail23 was generous enough to create this [auto format tool that write dollars for you](https://github.com/snailsoft/format) 32 | 33 | ## Build options 34 | - Dollar is enabled by default. Compile with `-D$=` to disable it. 35 | - Define `DOLLAR_USE_OPENMP` to use OpenMP timers (instead of `` timers) 36 | - Define `DOLLAR_MAX_TRACES` to change number of maximum instrumented samples (default: 512). 37 | - Define `DOLLAR_CPUMETER_WIDTH` to change width of CPU meter bars (default: 10 characters). 38 | 39 | ## Upcoming 40 | - std::vector (graph? opengl?) 41 | - "name %d" format 42 | 43 | ## Showcase 44 | ```c++ 45 | ~/dollar> cat ./sample.cc 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include "dollar.hpp" 52 | 53 | void x( int counter ) { $ 54 | while( counter-- > 0 ) { $ 55 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 56 | } 57 | } 58 | void c( int counter ) { $ 59 | while( counter-- > 0 ) { $ 60 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 61 | } 62 | } 63 | void y( int counter ) { $ 64 | while( counter-- > 0 ) { $ 65 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 66 | if( counter % 2 ) c(counter); else x(counter); 67 | } 68 | } 69 | void a( int counter ) { $ 70 | while( counter-- > 0 ) { $ 71 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 72 | y(counter); 73 | } 74 | } 75 | 76 | int main() { $ 77 | a(10); 78 | 79 | // write tracing results to a json file (for chrome://tracing embedded profiler) 80 | std::ofstream file("chrome.json"); 81 | dollar::chrome(file); 82 | 83 | // display results 84 | dollar::text(std::cout); 85 | 86 | // clear next frame 87 | dollar::clear(); 88 | } 89 | 90 | ~/dollar> g++ sample.cc -std=c++11 && ./a.out 91 | 1. +-main (./dollar.hpp:535) [..........] 0.00% CPU ( 0.000ms) 1 hits 92 | 2. +-a (./dollar.hpp:528) [..........] 0.00% CPU ( 0.000ms) 1 hits 93 | 3. +-a (./dollar.hpp:529) [..........] 5.71% CPU ( 20.000ms) 10 hits 94 | 4. +-y (./dollar.hpp:522) [..........] 0.00% CPU ( 0.000ms) 10 hits 95 | 5. +-y (./dollar.hpp:523) [==........] 25.71% CPU ( 90.000ms) 45 hits 96 | 6. |-c (./dollar.hpp:517) [..........] 0.00% CPU ( 0.000ms) 20 hits 97 | 7. | +-c (./dollar.hpp:518) [===.......] 34.29% CPU ( 120.000ms) 60 hits 98 | 8. +-x (./dollar.hpp:512) [..........] 0.00% CPU ( 0.000ms) 25 hits 99 | 9. +-x (./dollar.hpp:513) [===.......] 34.29% CPU ( 120.000ms) 60 hits 100 | 101 | ~/dollar> g++ sample.cc -std=c++11 -D$= && ./a.out 102 | ~/dollar> 103 | ``` 104 | 105 | ![image](https://raw.githubusercontent.com/r-lyeh/depot/master/dollar.png) 106 | 107 | ## Changelog 108 | - v1.2.0 (2016/10/03): Add chrome://tracing profiler support; Project renamed 109 | - v1.1.0 (2016/05/03): New tree view and CPU meters (ofxProfiler style); Smaller implementation; 110 | - v1.0.1 (2015/11/15): Fix win32 `max()` macro conflict 111 | - v1.0.0 (2015/08/02): Macro renamed 112 | - v0.0.0 (2015/03/13): Initial commit 113 | -------------------------------------------------------------------------------- /demo.cc: -------------------------------------------------------------------------------- 1 | #define DOLLAR_BUILD_DEMO 2 | #include "dollar.hpp" 3 | -------------------------------------------------------------------------------- /dollar.cpp: -------------------------------------------------------------------------------- 1 | #include "dollar.hpp" 2 | -------------------------------------------------------------------------------- /dollar.hpp: -------------------------------------------------------------------------------- 1 | // Dollar is a generic instrumented CPU profiler (C++11), header-only and zlib/libpng licensed. 2 | // Dollar outputs traces for chrome:://tracing and also ASCII, CSV, TSV and Markdown text formats. 3 | // - rlyeh ~~ listening to Team Ghost / High hopes. 4 | 5 | /* usage: 6 | #include "dollar.hpp" // dollar is enabled by default. compile with -D$= to disable any profiling 7 | int main() { $ // <-- put a dollar after every curly brace to determinate cpu cost of the scope 8 | for( int x = 0; x < 10000000; ++x ) { $ // <-- functions or loops will apply too 9 | // slow stuff... 10 | } 11 | std::ofstream file("chrome.json"); 12 | dollar::chrome(file); // write tracing results to a json file (for chrome://tracing embedded profiler) 13 | dollar::text(std::cout); // report stats to std::cout in text format; see also csv(), tsv() and markdown() 14 | dollar::clear(); // clear all scopes (like when entering a new frame) 15 | } 16 | */ 17 | 18 | #pragma once 19 | 20 | #define DOLLAR_VERSION "1.2.0" /* (2016/10/03) Add chrome://tracing profiler support; Project renamed; 21 | #define DOLLAR_VERSION "1.1.0" /* (2016/05/03) New tree view and CPU meters (ofxProfiler style); Smaller implementation; 22 | #define DOLLAR_VERSION "1.0.1" // (2015/11/15) Fix win32 `max()` macro conflict 23 | #define DOLLAR_VERSION "1.0.0" // (2015/08/02) Macro renamed 24 | #define DOLLAR_VERSION "0.0.0" // (2015/03/13) Initial commit */ 25 | 26 | #ifdef $ 27 | 28 | #include 29 | 30 | namespace dollar { 31 | 32 | inline void csv( std::ostream &cout ) {} 33 | inline void tsv( std::ostream &cout ) {} 34 | inline void text( std::ostream &cout ) {} 35 | inline void chrome( std::ostream &cout ) {} 36 | inline void markdown( std::ostream &cout ) {} 37 | inline void pause( bool paused ) {} 38 | inline bool is_paused() {} 39 | inline void clear() {} 40 | 41 | }; 42 | 43 | #else 44 | #include 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | 54 | #ifdef DOLLAR_USE_OMP 55 | # include 56 | #else 57 | # include 58 | #endif 59 | 60 | #ifdef _MSC_VER 61 | #include 62 | #else 63 | #include 64 | #endif 65 | 66 | #ifndef DOLLAR_MAX_TRACES 67 | #define DOLLAR_MAX_TRACES 512 68 | #endif 69 | 70 | #ifndef DOLLAR_CPUMETER_WIDTH 71 | #define DOLLAR_CPUMETER_WIDTH 10 72 | #endif 73 | 74 | #define DOLLAR_GLUE(a,b) a##b 75 | #define DOLLAR_JOIN(a,b) DOLLAR_GLUE(a,b) 76 | #define DOLLAR_UNIQUE(sym) DOLLAR_JOIN(sym, __LINE__) 77 | #define DOLLAR_STRINGIFY(x) #x 78 | #define DOLLAR_TOSTRING(x) DOLLAR_STRINGIFY(x) 79 | 80 | #ifdef _MSC_VER 81 | #define DOLLAR(name) dollar::sampler DOLLAR_UNIQUE(dollar_sampler_)(name); 82 | #define $ dollar::sampler DOLLAR_UNIQUE(dollar_sampler_)(std::string(__FUNCTION__) + " (" __FILE__ ":" DOLLAR_TOSTRING(__LINE__) ")" ); 83 | #else 84 | #define DOLLAR(name) dollar::sampler DOLLAR_UNIQUE(dollar_sampler_)(name); 85 | #define $ dollar::sampler DOLLAR_UNIQUE(dollar_sampler_)(std::string(__PRETTY_FUNCTION__) + " (" __FILE__ ":" DOLLAR_TOSTRING(__LINE__) ")" ); 86 | #endif 87 | 88 | namespace dollar 89 | { 90 | template < typename T > 91 | inline T* singleton() { 92 | static T tVar; 93 | return &tVar; 94 | } 95 | inline double now() { 96 | # ifdef DOLLAR_USE_OMP 97 | static auto const epoch = omp_get_wtime(); 98 | return omp_get_wtime() - epoch; 99 | # else 100 | static auto const epoch = std::chrono::steady_clock::now(); // milli ms > micro us > nano ns 101 | return std::chrono::duration_cast< std::chrono::microseconds >( std::chrono::steady_clock::now() - epoch ).count() / 1000000.0; 102 | # endif 103 | }; 104 | inline std::vector< std::string > tokenize( const std::string &self, const std::string &delimiters ) { 105 | unsigned char map [256] = {}; 106 | for( const unsigned char &ch : delimiters ) { 107 | map[ ch ] = '\1'; 108 | } 109 | std::vector< std::string > tokens(1); 110 | for( const unsigned char &ch : self ) { 111 | /**/ if( !map[ch] ) tokens.back().push_back( char(ch) ); 112 | else if( tokens.back().size() ) tokens.push_back( std::string() ); 113 | } 114 | while( tokens.size() && !tokens.back().size() ) tokens.pop_back(); 115 | return tokens; 116 | } 117 | template 118 | struct Node { 119 | std::string name; 120 | info *value; 121 | std::vector children; 122 | 123 | Node( const std::string &name, info *value = 0 ) : name(name), value(value) 124 | {} 125 | 126 | void tree_printer( std::string indent, bool leaf, std::ostream &out ) const { 127 | if( leaf ) { 128 | out << indent << "+-" << name << std::endl; 129 | indent += " "; 130 | } else { 131 | out << indent << "|-" << name << std::endl; 132 | indent += "| "; 133 | } 134 | for( auto end = children.size(), it = end - end; it < end; ++it ) { 135 | children[it].tree_printer( indent, it == (end - 1), out ); 136 | } 137 | } 138 | void tree_printer( std::ostream &out = std::cout ) const { 139 | tree_printer( "", true, out ); 140 | } 141 | Node&tree_recreate_branch( const std::vector &names ) { 142 | auto *where = &(*this); 143 | for( auto &name : names ) { 144 | bool found = false; 145 | for( auto &it : where->children ) { 146 | if( it.name == name ) { 147 | where = ⁢ 148 | found = true; 149 | break; 150 | } 151 | } 152 | if( !found ) { 153 | where->children.push_back( Node(name) ); 154 | where = &where->children.back(); 155 | } 156 | } 157 | return *where; 158 | } 159 | template 160 | void tree_walker( const FN0 &method, const FN1 &pre_children, const FN2 &post_chilren ) const { 161 | if( children.empty() ) { 162 | method( *this ); 163 | } else { 164 | pre_children( *this ); 165 | for( auto &child : children ) { 166 | child.tree_walker( method, pre_children, post_chilren ); 167 | } 168 | post_chilren( *this ); 169 | } 170 | } 171 | }; 172 | class profiler 173 | { 174 | std::vector stack; 175 | bool paused; 176 | 177 | public: 178 | 179 | struct info { 180 | bool paused = false; 181 | unsigned hits = 0; 182 | double current = 0, total = 0; 183 | #ifdef _MSC_VER 184 | int pid = 0; 185 | #else 186 | pid_t pid = 0; 187 | #endif 188 | std::thread::id tid; 189 | std::string title; 190 | 191 | info() 192 | {} 193 | 194 | info( const std::string &title ) : title(title) 195 | {} 196 | 197 | inline friend 198 | std::ostream &operator<<( std::ostream &os, const info &k ) { 199 | os << "title:" << tokenize(k.title, ";").back() << std::endl; 200 | os << "paused:" << k.paused << std::endl; 201 | os << "hits:" << k.hits << std::endl; 202 | os << "current:" << k.current << std::endl; 203 | os << "total:" << k.total << std::endl; 204 | os << "pid:" << k.pid << std::endl; 205 | os << "tid:" << k.tid << std::endl; 206 | return os; 207 | } 208 | }; 209 | 210 | profiler() { 211 | stack.reserve( DOLLAR_MAX_TRACES ); 212 | } 213 | 214 | info &in( const std::string &title ) { 215 | #ifdef _MSC_VER 216 | auto pid = _getpid(); 217 | #else 218 | auto pid = getpid(); 219 | #endif 220 | auto tid = std::this_thread::get_id(); 221 | 222 | //std::stringstream header; 223 | //header << pid << "/" << tid << "/" << title; 224 | //stack.push_back( stack.empty() ? header.str() : stack.back() + ";" + header.str() ); 225 | stack.push_back( stack.empty() ? title : stack.back() + ";" + title ); 226 | 227 | auto &id = stack.back(); 228 | 229 | if( counters.find( id ) == counters.end() ) { 230 | counters[ id ] = info ( stack.back() ); 231 | } 232 | 233 | auto &sample = counters[ id ]; 234 | 235 | sample.hits ++; 236 | sample.current = -dollar::now(); 237 | 238 | sample.pid = pid; 239 | sample.tid = tid; 240 | 241 | return sample; 242 | } 243 | 244 | void out( info &sample ) { 245 | sample.current += dollar::now(); 246 | sample.total += ( sample.paused ? 0.f : sample.current ); 247 | stack.pop_back(); 248 | } 249 | 250 | template 251 | void print( std::ostream &out, const char *tab = ",", const char *feed = "\r\n" ) const { 252 | auto inital_matches = []( const std::string &text, const std::string &abc ) -> unsigned { 253 | unsigned c = 0; 254 | for( auto end = (std::min)(text.size(), abc.size()), it = end - end; it < end; ++it, ++c ) { 255 | if( text[it] != abc[it] ) break; 256 | } 257 | return c; 258 | }; 259 | auto starts_with = [&]( const std::string &text, const std::string &abc ) -> bool { 260 | return inital_matches( text, abc ) == abc.size(); 261 | }; 262 | 263 | // create a copy of the class to modify it, so this method is still const 264 | auto copy = *this; 265 | 266 | // finish any active scope 267 | while( !copy.stack.empty() ) { 268 | auto ¤t = copy.counters[ stack.back() ]; 269 | copy.out( current ); 270 | } 271 | 272 | // update time hierarchically 273 | { 274 | // sorted tree 275 | std::vector< std::pair > az_tree; 276 | 277 | for( auto &it : copy.counters ) { 278 | auto &info = it.second; 279 | az_tree.emplace_back( info.title, &info ); 280 | } 281 | 282 | std::sort( az_tree.begin(), az_tree.end() ); 283 | std::reverse( az_tree.begin(), az_tree.end() ); 284 | 285 | // here's the magic 286 | for( size_t i = 0; i < az_tree.size(); ++i ) { 287 | for( size_t j = i + 1; j < az_tree.size(); ++j ) { 288 | if( starts_with( az_tree[ i ].first, az_tree[ j ].first ) ) { 289 | az_tree[ j ].second->total -= az_tree[ i ].second->total; 290 | } 291 | } 292 | } 293 | } 294 | 295 | // calculate total accumulated time 296 | double total = 0; 297 | for( auto &it : copy.counters ) { 298 | total += it.second.total; 299 | } 300 | 301 | std::vector list; 302 | 303 | // string2tree { 304 | static unsigned char pos = 0; 305 | info dummy; 306 | dummy.title = "/"; 307 | #ifdef _MSC_VER 308 | dummy.pid = _getpid(); 309 | #else 310 | dummy.pid = getpid(); 311 | #endif 312 | dummy.tid = std::this_thread::get_id(); 313 | Node root( std::string() + "\\|/-"[(++pos)%4], &dummy ); 314 | for( auto it = copy.counters.begin(), end = copy.counters.end(); it != end; ++it ) { 315 | auto &info = it->second; 316 | list.push_back( info.title ); 317 | 318 | auto split = tokenize( info.title, ";" ); 319 | 320 | auto &node = root.tree_recreate_branch( split ); 321 | node.value = &info; 322 | } 323 | std::stringstream ss; 324 | root.tree_printer( ss ); 325 | list = tokenize( ss.str(), "\r\n" ); 326 | static size_t maxlen = 0; 327 | for( auto &it : list ) { 328 | maxlen = (std::max)(maxlen, it.size()); 329 | } 330 | for( auto &it : list ) { 331 | /**/ if( maxlen > it.size() ) it += std::string( maxlen - it.size(), ' ' ); 332 | else if( maxlen < it.size() ) it.resize( maxlen ); 333 | } 334 | // } 335 | 336 | // prettify name/titles 337 | size_t i = 0; 338 | if( for_chrome ) { 339 | for( auto &cp : copy.counters ) { 340 | cp.second.title = tokenize( cp.second.title, ";" ).back(); 341 | for( auto &ch : cp.second.title ) { 342 | if( ch == '\\' ) ch = '/'; 343 | } 344 | } 345 | } else { 346 | size_t x = 0; 347 | for( auto &cp : copy.counters ) { 348 | cp.second.title = list[++x]; 349 | for( auto &ch : cp.second.title ) { 350 | if( ch == '\\' ) ch = '/'; 351 | } 352 | } 353 | } 354 | 355 | if( !for_chrome ) { 356 | std::string format, sep, graph, buffer(1024, '\0'); 357 | // pre-loop 358 | for( auto &it : std::vector{ "%4d.","%s","[%s]","%5.2f%% CPU","(%9.3fms)","%5d hits",feed } ) { 359 | format += sep + it; 360 | sep = tab; 361 | } 362 | // loop 363 | for( auto &it : copy.counters ) { 364 | auto &info = it.second; 365 | double cpu = info.total * 100.0 / total; 366 | int width(cpu*DOLLAR_CPUMETER_WIDTH/100); 367 | graph = std::string( width, '=' ) + std::string( DOLLAR_CPUMETER_WIDTH - width, '.' ); 368 | #ifdef _MSC_VER 369 | sprintf_s( &buffer[0], 1024, 370 | #else 371 | sprintf( &buffer[0], 372 | #endif 373 | format.c_str(), ++i, it.second.title.c_str(), graph.c_str(), cpu, (float)(info.total * 1000), info.hits ); 374 | out << &buffer[0]; 375 | } 376 | } else { 377 | 378 | // setup 379 | out << "[" << std::endl; 380 | 381 | // json array format 382 | // [ref] https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview 383 | // [ref] https://github.com/catapult-project/catapult/blob/master/tracing/tracing/base/color_scheme.html#L54 384 | 385 | auto get_color = []( float pct ) { 386 | return pct <= 16 ? "good": 387 | pct <= 33 ? "bad": 388 | "terrible"; 389 | }; 390 | 391 | double timestamp = 0; 392 | root.tree_walker( 393 | [&]( const Node &node ) { 394 | auto &info = *node.value; 395 | double cpu = info.total * 100.0 / total; 396 | out << "{\"name\": \"" << info.title << "\"," 397 | "\"cat\": \"" << "CPU,DOLLAR" << "\"," 398 | "\"ph\": \"" << 'X' << "\"," 399 | "\"pid\": " << info.pid << "," 400 | "\"tid\": " << info.tid << "," 401 | "\"ts\": " << (unsigned int)(timestamp * 1000 * 1000) << "," 402 | "\"dur\": " << (unsigned int)(info.total * 1000 * 1000) << "," 403 | "\"cname\": \"" << get_color(cpu) << "\"" "," << 404 | "\"args\": {}},\n"; 405 | timestamp += info.total; 406 | }, 407 | [&]( const Node &node ) { 408 | auto &info = *node.value; 409 | double cpu = info.total * 100.0 / total; 410 | out << "{\"name\": \"" << info.title << "\"," 411 | "\"cat\": \"" << "CPU,DOLLAR" << "\"," 412 | "\"ph\": \"" << 'B' << "\"," 413 | "\"pid\": " << info.pid << "," 414 | "\"tid\": " << info.tid << "," 415 | "\"ts\": " << (unsigned int)(timestamp * 1000 * 1000) << "," 416 | "\"args\": {}},\n"; 417 | timestamp += info.total; 418 | }, 419 | [&]( const Node &node ) { 420 | auto &info = *node.value; 421 | double cpu = info.total * 100.0 / total; 422 | out << "{\"name\": \"" << info.title << "\"," 423 | "\"cat\": \"" << "CPU,DOLLAR" << "\"," 424 | "\"ph\": \"" << 'E' << "\"," 425 | "\"pid\": " << info.pid << "," 426 | "\"tid\": " << info.tid << "," 427 | "\"ts\": " << (unsigned int)((timestamp + info.total) * 1000 * 1000) << "," 428 | "\"cname\": \"" << get_color(cpu) << "\"" "," << 429 | "\"args\": {}},\n"; 430 | timestamp += info.total; 431 | } ); 432 | } 433 | } 434 | 435 | void pause( bool paused_ ) { 436 | paused = paused_; 437 | } 438 | 439 | bool is_paused() const { 440 | return paused; 441 | } 442 | 443 | void clear() { 444 | bool p = paused; 445 | auto num_unfinished_scopes = stack.size(); 446 | *this = profiler(); 447 | stack.resize( num_unfinished_scopes ); 448 | paused = p; 449 | } 450 | 451 | private: std::map< std::string, info > counters; 452 | }; 453 | 454 | class sampler { 455 | sampler(); 456 | sampler( const sampler & ); 457 | sampler& operator=( const sampler & ); 458 | profiler::info *handle; 459 | 460 | public: // public api 461 | 462 | explicit sampler( const std::string &title ) { 463 | handle = &singleton()->in( title ); 464 | } 465 | 466 | ~sampler() { 467 | singleton()->out( *handle ); 468 | } 469 | }; 470 | 471 | inline void csv( std::ostream &os ) { 472 | singleton()->print<0>(os, ","); 473 | } 474 | 475 | inline void tsv( std::ostream &os ) { 476 | singleton()->print<0>(os, "\t"); 477 | } 478 | 479 | inline void markdown( std::ostream &os ) { 480 | singleton()->print<0>(os, "|"); 481 | } 482 | 483 | inline void text( std::ostream &os ) { 484 | singleton()->print<0>(os, " "); 485 | } 486 | 487 | inline void chrome( std::ostream &os ) { 488 | singleton()->print<1>(os, ""); 489 | } 490 | 491 | inline void pause( bool paused ) { 492 | singleton()->pause( paused ); 493 | } 494 | 495 | inline bool is_paused() { 496 | return singleton()->is_paused(); 497 | } 498 | 499 | inline void clear() { 500 | singleton()->clear(); 501 | } 502 | } 503 | 504 | #endif 505 | 506 | #ifdef DOLLAR_BUILD_DEMO 507 | #include 508 | #include 509 | #include 510 | #include 511 | 512 | void x( int counter ) { $ 513 | while( counter-- > 0 ) { $ 514 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 515 | } 516 | } 517 | void c( int counter ) { $ 518 | while( counter-- > 0 ) { $ 519 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 520 | } 521 | } 522 | void y( int counter ) { $ 523 | while( counter-- > 0 ) { $ 524 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 525 | if( counter % 2 ) c(counter); else x(counter); 526 | } 527 | } 528 | void a( int counter ) { $ 529 | while( counter-- > 0 ) { $ 530 | std::this_thread::sleep_for( std::chrono::microseconds( int(0.00125 * 1000000) ) ); 531 | y(counter); 532 | } 533 | } 534 | 535 | int main() { $ 536 | a(10); 537 | 538 | // write tracing results to a json file (for chrome://tracing embedded profiler) 539 | std::ofstream file("chrome.json"); 540 | dollar::chrome(file); 541 | 542 | // display ascii text results 543 | dollar::text(std::cout); 544 | 545 | // clear next frame 546 | dollar::clear(); 547 | } 548 | #endif 549 | --------------------------------------------------------------------------------