├── .gitignore ├── Profiny.h ├── Profiny.vcxproj ├── ReadMe.txt └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | Debug/* 2 | Release/* 3 | Profiny.vcxproj.user 4 | profiny.out -------------------------------------------------------------------------------- /Profiny.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Profiny - Lightweight Profiler Tool 3 | * Copyright (C) 2013 Sercan Tutar 4 | * 5 | * This Source Code Form is subject to the terms of the Mozilla Public 6 | * License, v. 2.0. If a copy of the MPL was not distributed with this 7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 | * 9 | * 10 | * USAGE: 11 | * First PROFINY_CALL_GRAPH_PROFILER or PROFINY_FLAT_PROFILER must be defined 12 | * (giving as a compiler option is advised). If you; 13 | * 14 | * - define PROFINY_CALL_GRAPH_PROFILER, it will work as a call-graph profiler 15 | * - define PROFINY_FLAT_PROFILER, it will work as a flat profiler 16 | * - define neither, Profiny macros will be set to blank (i.e. profiling will be off) 17 | * - define both, it will give an error 18 | * 19 | * Later, if you chose PROFINY_CALL_GRAPH_PROFILER, you may want to determine 20 | * whether recursive calls will be omitted or not (omitted by default) by calling: 21 | * 22 | * Profiler::setOmitRecursiveCalls(bool) 23 | * 24 | * By default (if the profiling is not off), if your program exits normally, Profinity 25 | * will print results in "profinity.out" file. Also, the user can force printing results 26 | * at any time by calling: 27 | * 28 | * Profiler::printStats("filename") 29 | * 30 | * See ReadMe.txt for more info. 31 | * 32 | * 33 | * Happy profiling! 34 | * 35 | */ 36 | 37 | 38 | #ifndef PROFINY_H_ 39 | #define PROFINY_H_ 40 | 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | 49 | #ifdef _WIN32 50 | #include 51 | #else 52 | #include 53 | #endif 54 | 55 | 56 | #if defined(PROFINY_CALL_GRAPH_PROFILER) && defined(PROFINY_FLAT_PROFILER) 57 | 58 | # error "PROFINY_CALL_GRAPH_PROFILER and PROFINY_FLAT_PROFILER should not be defined at the same time!" 59 | 60 | #elif defined(PROFINY_CALL_GRAPH_PROFILER) || defined(PROFINY_FLAT_PROFILER) 61 | 62 | # define PROFINY_SCOPE \ 63 | std::ostringstream _oss; \ 64 | _oss << /*__FILE__ << ":" << */__FUNCTION__ << ":" << __LINE__; \ 65 | profiny::ScopedProfile _sco_pro(_oss.str()); 66 | 67 | # define PROFINY_SCOPE_WITH_ID(ID) \ 68 | std::ostringstream _oss; \ 69 | _oss << /*__FILE__ << ":" << */__FUNCTION__ << ":" << __LINE__ << ":" << (ID); \ 70 | profiny::ScopedProfile _sco_pro(_oss.str()); 71 | 72 | # define PROFINY_NAMED_SCOPE(NAME) \ 73 | std::ostringstream _oss; \ 74 | _oss << (NAME); \ 75 | profiny::ScopedProfile _sco_pro(_oss.str()); 76 | 77 | # define PROFINY_NAMED_SCOPE_WITH_ID(NAME, ID) \ 78 | std::ostringstream _oss; \ 79 | _oss << (NAME) << ":" << (ID); \ 80 | profiny::ScopedProfile _sco_pro(_oss.str()); 81 | 82 | #else 83 | 84 | # define PROFINY_SCOPE 85 | 86 | # define PROFINY_SCOPE_WITH_ID(ID) 87 | 88 | # define PROFINY_NAMED_SCOPE(NAME) 89 | 90 | # define PROFINY_NAMED_SCOPE_WITH_ID(NAME, ID) 91 | 92 | #endif 93 | 94 | 95 | #define NANOSEC_TO_SEC(X) ((X) / 1000000000.0) 96 | 97 | 98 | namespace profiny 99 | { 100 | class Timer 101 | { 102 | public: 103 | Timer(); 104 | 105 | void start(); 106 | 107 | void stop(); 108 | 109 | double getElapsedTime(); 110 | 111 | private: 112 | double m_startTime; 113 | 114 | double m_stopTime; 115 | 116 | bool m_running; 117 | 118 | #ifdef _WIN32 119 | double m_reciprocalFrequency; 120 | #endif 121 | 122 | double getTime(); 123 | }; 124 | 125 | /**********************************************************************/ 126 | 127 | class BaseObject 128 | { 129 | public: 130 | BaseObject(); 131 | 132 | virtual ~BaseObject(); 133 | 134 | void incrRef(); 135 | 136 | void decrRef(); 137 | 138 | int getRef() const; 139 | 140 | private: 141 | int m_ref; 142 | }; 143 | 144 | /**********************************************************************/ 145 | 146 | class Profile : public BaseObject 147 | { 148 | friend class ScopedProfile; 149 | friend class Profiler; 150 | 151 | private: 152 | Profile(const std::string& name); 153 | 154 | ~Profile(); 155 | 156 | bool start(); 157 | 158 | bool stop(); 159 | 160 | unsigned int getCallCount() const; 161 | 162 | std::string getName() const; 163 | 164 | void getTimes(double& wall) const; 165 | 166 | #ifdef PROFINY_CALL_GRAPH_PROFILER 167 | std::map& getSubProfiles(); 168 | 169 | std::map m_subProfiles; 170 | #else 171 | bool m_timeStarted; 172 | #endif 173 | 174 | std::string m_name; 175 | 176 | unsigned int m_callCount; 177 | 178 | double m_wallTime; 179 | 180 | Timer m_timer; 181 | }; 182 | 183 | /**********************************************************************/ 184 | 185 | class ScopedProfile : public BaseObject 186 | { 187 | public: 188 | ScopedProfile(const std::string& name); 189 | 190 | ~ScopedProfile(); 191 | 192 | private: 193 | Profile* m_profile; 194 | }; 195 | 196 | /**********************************************************************/ 197 | 198 | class Profiler : public BaseObject 199 | { 200 | friend class Profile; 201 | friend class ScopedProfile; 202 | 203 | public: 204 | static void printStats(const std::string& filename); 205 | 206 | #ifdef PROFINY_CALL_GRAPH_PROFILER 207 | static void setOmitRecursiveCalls(bool omit); 208 | 209 | static bool getOmitRecursiveCalls(); 210 | #endif 211 | 212 | private: 213 | Profiler(); 214 | 215 | ~Profiler(); 216 | 217 | static Profiler* getInstance(); 218 | 219 | Profile* getProfile(const std::string& name); 220 | 221 | static void printStats(); 222 | 223 | static void printStats(std::ofstream& fs, std::map* p, int depth); 224 | 225 | #ifdef PROFINY_CALL_GRAPH_PROFILER 226 | std::map& getCurrentProfilesRoot(); 227 | 228 | void pushProfile(Profile* p); 229 | 230 | void popProfile(); 231 | 232 | bool isInStack(const std::string& name); 233 | #endif 234 | 235 | std::map m_profiles; 236 | 237 | static Profiler* m_instance; 238 | 239 | #ifdef PROFINY_CALL_GRAPH_PROFILER 240 | std::vector m_profileStack; 241 | 242 | bool m_omitRecursiveCalls; 243 | #endif 244 | }; 245 | 246 | /**********************************************************************/ 247 | 248 | Timer::Timer() 249 | : m_startTime(0.0f), m_stopTime(0.0f), m_running(false) 250 | { 251 | #ifdef _WIN32 252 | LARGE_INTEGER freq; 253 | QueryPerformanceFrequency(&freq); 254 | m_reciprocalFrequency = 1.0f / freq.QuadPart; 255 | #endif 256 | } 257 | 258 | void Timer::start() 259 | { 260 | m_running = true; 261 | m_startTime = getTime(); 262 | } 263 | 264 | void Timer::stop() 265 | { 266 | m_running = false; 267 | m_stopTime = getTime() - m_startTime; 268 | } 269 | 270 | double Timer::getElapsedTime() 271 | { 272 | if (m_running) 273 | return getTime() - m_startTime; 274 | 275 | return m_stopTime; 276 | } 277 | 278 | double Timer::getTime() 279 | { 280 | #ifdef _WIN32 281 | LARGE_INTEGER count; 282 | QueryPerformanceCounter(&count); 283 | double time = count.QuadPart * m_reciprocalFrequency; 284 | #else 285 | struct timeval interval; 286 | clock_gettime(CLOCK_MONOTONIC, &interval); 287 | double time = interval.tv_sec + interval.tv_usec * 0.000001f; 288 | #endif 289 | 290 | return time; 291 | }; 292 | 293 | /**********************************************************************/ 294 | 295 | inline BaseObject::BaseObject() : 296 | m_ref(0) 297 | { 298 | } 299 | 300 | inline BaseObject::~BaseObject() 301 | { 302 | } 303 | 304 | inline void BaseObject::incrRef() 305 | { 306 | ++m_ref; 307 | } 308 | 309 | inline void BaseObject::decrRef() 310 | { 311 | --m_ref; 312 | } 313 | 314 | inline int BaseObject::getRef() const 315 | { 316 | return m_ref; 317 | } 318 | 319 | /**********************************************************************/ 320 | 321 | inline Profile::Profile(const std::string& name) : 322 | #ifndef PROFINY_CALL_GRAPH_PROFILER 323 | m_timeStarted(false), 324 | #endif 325 | m_name(name), m_callCount(0), m_wallTime(0.0) 326 | { 327 | } 328 | 329 | inline Profile::~Profile() 330 | { 331 | } 332 | 333 | inline bool Profile::start() 334 | { 335 | #ifdef PROFINY_CALL_GRAPH_PROFILER 336 | Profiler::getInstance()->pushProfile(this); 337 | #else 338 | if (m_timeStarted) 339 | { 340 | return false; 341 | } 342 | m_timeStarted = true; 343 | #endif 344 | m_timer.start(); 345 | return true; 346 | } 347 | 348 | inline bool Profile::stop() 349 | { 350 | #ifdef PROFINY_CALL_GRAPH_PROFILER 351 | Profiler::getInstance()->popProfile(); 352 | #else 353 | if (!m_timeStarted) 354 | { 355 | return false; 356 | } 357 | m_timeStarted = false; 358 | #endif 359 | m_timer.stop(); // TODO: check if we need this line 360 | m_wallTime += m_timer.getElapsedTime(); 361 | ++m_callCount; 362 | return true; 363 | } 364 | 365 | inline unsigned int Profile::getCallCount() const 366 | { 367 | return m_callCount; 368 | } 369 | 370 | inline std::string Profile::getName() const 371 | { 372 | return m_name; 373 | } 374 | 375 | inline void Profile::getTimes(double& wall) const 376 | { 377 | wall = m_wallTime; 378 | } 379 | 380 | #ifdef PROFINY_CALL_GRAPH_PROFILER 381 | inline std::map& Profile::getSubProfiles() 382 | { 383 | return m_subProfiles; 384 | } 385 | #endif 386 | 387 | /**********************************************************************/ 388 | 389 | inline ScopedProfile::ScopedProfile(const std::string& name) : m_profile(NULL) 390 | { 391 | std::string n(name); 392 | 393 | #ifdef PROFINY_CALL_GRAPH_PROFILER 394 | if (Profiler::getInstance()->isInStack(n)) 395 | { // profile is already in stack (probably a recursive call) 396 | if (Profiler::getInstance()->getOmitRecursiveCalls()) 397 | { 398 | return; 399 | } 400 | else 401 | { 402 | n = "RECURSIVE@" + n; 403 | } 404 | } 405 | #endif 406 | 407 | m_profile = Profiler::getInstance()->getProfile(n); 408 | if (m_profile != NULL) 409 | { 410 | if (!m_profile->start()) 411 | { // cannot start profiler (probably a recursive call for flat profiler) 412 | delete m_profile; 413 | m_profile = NULL; 414 | } 415 | } 416 | else 417 | { 418 | std::cerr << "Cannot start scoped profiler: " << n << std::endl; 419 | } 420 | } 421 | 422 | inline ScopedProfile::~ScopedProfile() 423 | { 424 | if (m_profile != NULL) 425 | { 426 | m_profile->stop(); 427 | } 428 | } 429 | 430 | /**********************************************************************/ 431 | 432 | Profiler* Profiler::m_instance = NULL; 433 | 434 | inline Profiler::Profiler() 435 | #ifdef PROFINY_CALL_GRAPH_PROFILER 436 | : m_omitRecursiveCalls(true) 437 | #endif 438 | { 439 | } 440 | 441 | inline Profiler::~Profiler() 442 | { 443 | } 444 | 445 | inline Profiler* Profiler::getInstance() 446 | { 447 | if (m_instance == NULL) 448 | { 449 | m_instance = new Profiler; 450 | atexit(printStats); 451 | } 452 | return m_instance; 453 | } 454 | 455 | inline Profile* Profiler::getProfile(const std::string& name) 456 | { 457 | #ifdef PROFINY_CALL_GRAPH_PROFILER 458 | std::map& profiles = getCurrentProfilesRoot(); 459 | #else 460 | std::map& profiles = m_profiles; 461 | #endif 462 | std::map::iterator it = profiles.find(name); 463 | if (it != profiles.end()) 464 | { 465 | return it->second; 466 | } 467 | else 468 | { 469 | Profile* result = new Profile(name); 470 | profiles[name] = result; 471 | return result; 472 | } 473 | } 474 | 475 | #ifdef PROFINY_CALL_GRAPH_PROFILER 476 | inline std::map& Profiler::getCurrentProfilesRoot() 477 | { 478 | return m_profileStack.empty() ? m_profiles : m_profileStack.back()->getSubProfiles(); 479 | } 480 | 481 | inline void Profiler::pushProfile(Profile* p) 482 | { 483 | m_profileStack.push_back(p); 484 | } 485 | 486 | inline void Profiler::popProfile() 487 | { 488 | if (!m_profileStack.empty()) 489 | { 490 | m_profileStack.pop_back(); 491 | } 492 | } 493 | 494 | inline bool Profiler::isInStack(const std::string& name) 495 | { 496 | for (unsigned int i=0; igetName() == name) 499 | { 500 | return true; 501 | } 502 | } 503 | return false; 504 | } 505 | #endif 506 | 507 | inline void Profiler::printStats(std::ofstream& fs, std::map* p, int depth) 508 | { 509 | #ifdef PROFINY_CALL_GRAPH_PROFILER 510 | std::ostringstream oss; 511 | for (int i=0; ibegin(); it != p->end(); ++it) 518 | { 519 | unsigned int cc = it->second->getCallCount(); 520 | double wall; 521 | it->second->getTimes(wall); 522 | #ifdef PROFINY_CALL_GRAPH_PROFILER 523 | fs << oss.str() << it->second->getName() << " T(s):" << wall << " #:" << cc << " A(ms):" << wall * 1000 / cc << std::endl; 524 | printStats(fs, &(it->second->getSubProfiles()), depth+1); 525 | #else 526 | fs << it->second->getName() << " T(s):" << wall << " #:" << cc << " A(ms):" << wall * 1000 / cc << std::endl; 527 | #endif 528 | delete it->second; 529 | } 530 | } 531 | 532 | inline void Profiler::printStats() 533 | { 534 | printStats("profiny.out"); 535 | 536 | delete m_instance; 537 | m_instance = NULL; 538 | } 539 | 540 | inline void Profiler::printStats(const std::string& filename) 541 | { 542 | std::ofstream fs; 543 | fs.open(filename.c_str()); 544 | if (!fs.is_open()) 545 | { 546 | std::cerr << "Cannot open profiler output file: " << filename << std::endl; 547 | return; 548 | } 549 | Profiler::printStats(fs, &(getInstance()->m_profiles), 0); 550 | fs.close(); 551 | } 552 | 553 | #ifdef PROFINY_CALL_GRAPH_PROFILER 554 | inline void Profiler::setOmitRecursiveCalls(bool omit) 555 | { 556 | getInstance()->m_omitRecursiveCalls = omit; 557 | } 558 | 559 | inline bool Profiler::getOmitRecursiveCalls() 560 | { 561 | return getInstance()->m_omitRecursiveCalls; 562 | } 563 | #endif 564 | 565 | } // namespace profiny 566 | 567 | /**********************************************************************/ 568 | 569 | inline void intrusive_ptr_add_ref(profiny::BaseObject* p) 570 | { 571 | if (p != NULL) 572 | { // pointer is not NULL 573 | p->incrRef(); 574 | } 575 | } 576 | 577 | inline void intrusive_ptr_release(profiny::BaseObject* p) 578 | { 579 | if (p != NULL) 580 | { // pointer is not NULL 581 | p->decrRef(); 582 | if (p->getRef() <= 0) 583 | { // reference count is zero or less 584 | delete p; 585 | } 586 | } 587 | } 588 | 589 | #endif /* PROFINY_H_ */ 590 | -------------------------------------------------------------------------------- /Profiny.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {40ECEE37-662F-4801-B565-BC3EFA273F75} 15 | Win32Proj 16 | 17 | 18 | 19 | Application 20 | true 21 | v140 22 | 23 | 24 | Application 25 | false 26 | v140 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 43 | MultiThreadedDebugDLL 44 | Level3 45 | ProgramDatabase 46 | Disabled 47 | 48 | 49 | MachineX86 50 | true 51 | Console 52 | 53 | 54 | 55 | 56 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 57 | MultiThreadedDLL 58 | Level3 59 | ProgramDatabase 60 | 61 | 62 | MachineX86 63 | true 64 | Console 65 | true 66 | true 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /ReadMe.txt: -------------------------------------------------------------------------------- 1 | 2 | ***************************************************************************************** 3 | **************************************** Profiny **************************************** 4 | ***************************************************************************************** 5 | 6 | Profiny is a profiling helper tool which consists of just one header file. In order to 7 | use Profiny, you should install these libraries: 8 | 9 | boost version 1.52 (or newer) 10 | 11 | A sample code using Profiny can be written as follows: 12 | 13 | #include "Profiny.h" 14 | 15 | int f(int n) { 16 | PROFINY_SCOPE 17 | int result = 1; 18 | for (int i=1; i::". It is 123 | also possible to use these macros: 124 | 125 | PROFINY_SCOPE: This is the simplest macro. The profile name for this macro is as 126 | follows: 127 | :: 128 | 129 | PROFINY_SCOPE_WITH_ID(ID): This macro can be used where we want to differentiate 130 | profiles of a single scope by giving an ID to each. For example, we can 131 | use this macro if we want to profile every instance of a class separately. 132 | If we have a unique "m_name" member in the class, we can simply write: 133 | PROFINY_SCOPE_WITH_ID(m_name) 134 | to the beginning of each function, and profile methods of the instances 135 | (not the class' method). The name of each profile will be: 136 | ::: 137 | 138 | PROFINY_NAMED_SCOPE(NAME): This macro does not automatically generate profile 139 | name, instead, it takes it from the user. So, the profile name is as 140 | follows: 141 | 142 | 143 | PROFINY_NAMED_SCOPE_WITH_ID(NAME, ID): This macro uses name and ID given as 144 | arguments and constructs profile name as follows: 145 | : 146 | 147 | Later, if you chose PROFINY_CALL_GRAPH_PROFILER, you may want to determine whether 148 | recursive calls will be omitted or not (omitted by default) by calling: 149 | 150 | profiny::Profiler::setOmitRecursiveCalls(bool) 151 | 152 | If call-graph profiling requested and recursive calls are not omitted the results will be 153 | as follows (including recursive calls): 154 | 155 | ../main.cpp:main:45 T:2.58801 #:1 %:99.3041 156 | ../main.cpp:f:8 T:2.58789 #:1 %:99.3086 157 | ../main.cpp:g:19 T:4.4433e-05 #:1 %:0 158 | RECURSIVE@../main.cpp:g:19 T:2.9165e-05 #:1 %:0 159 | RECURSIVE@../main.cpp:g:19 T:2.5138e-05 #:1 %:0 160 | ../main.cpp:h1:29 T:3.0788e-05 #:1 %:0 161 | ../main.cpp:h2:37 T:2.7134e-05 #:1 %:0 162 | RECURSIVE@../main.cpp:h1:29 T:2.3411e-05 #:1 %:0 163 | RECURSIVE@../main.cpp:h2:37 T:1.9694e-05 #:1 %:0 164 | RECURSIVE@../main.cpp:h1:29 T:1.6154e-05 #:1 %:0 165 | RECURSIVE@../main.cpp:h2:37 T:1.2546e-05 #:1 %:0 166 | 167 | Happy profiling! 168 | 169 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #define PROFINY_CALL_GRAPH_PROFILER 2 | //#define PROFINY_FLAT_PROFILER 3 | 4 | #include "Profiny.h" 5 | 6 | int f(int n) 7 | { 8 | PROFINY_SCOPE 9 | int result = 1; 10 | for (int i=1; i