From 28f88875170b0b6b86f7685e4b6c9b6cdf02affd Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Mon, 22 Feb 2010 12:53:47 +0000 Subject: [PATCH] * Made ProfileResult and ImageProfileResults BReferenceable. * Added command line option '-S', which triggers a new summary mode. When enabled the image/symbol hits aren't counted for individual threads anymore, but summed up for all threads. The results are printed at the end. Works together with all profiling modes (inclusive, exclusive, callgrind). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35573 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/bin/debug/profile/BasicProfileResult.cpp | 19 ++- src/bin/debug/profile/BasicProfileResult.h | 7 +- .../debug/profile/CallgrindProfileResult.cpp | 20 ++- .../debug/profile/CallgrindProfileResult.h | 7 +- src/bin/debug/profile/Jamfile | 1 + src/bin/debug/profile/Options.h | 4 +- src/bin/debug/profile/ProfileResult.h | 13 +- .../debug/profile/SummaryProfileResult.cpp | 148 ++++++++++++++++++ src/bin/debug/profile/SummaryProfileResult.h | 94 +++++++++++ src/bin/debug/profile/Thread.cpp | 33 ++-- src/bin/debug/profile/profile.cpp | 136 +++++++++++++--- 11 files changed, 430 insertions(+), 52 deletions(-) create mode 100644 src/bin/debug/profile/SummaryProfileResult.cpp create mode 100644 src/bin/debug/profile/SummaryProfileResult.h diff --git a/src/bin/debug/profile/BasicProfileResult.cpp b/src/bin/debug/profile/BasicProfileResult.cpp index ae3928be43..15e8fa3f85 100644 --- a/src/bin/debug/profile/BasicProfileResult.cpp +++ b/src/bin/debug/profile/BasicProfileResult.cpp @@ -220,10 +220,23 @@ BasicProfileResult::PrintResults(ImageProfileResultContainer* container) } -ImageProfileResult* -BasicProfileResult::CreateImageProfileResult(SharedImage* image, image_id id) +status_t +BasicProfileResult::GetImageProfileResult(SharedImage* image, image_id id, + ImageProfileResult*& _imageResult) { - return new(std::nothrow) BasicImageProfileResult(image, id); + BasicImageProfileResult* result + = new(std::nothrow) BasicImageProfileResult(image, id); + if (result == NULL) + return B_NO_MEMORY; + + status_t error = result->Init(); + if (error != B_OK) { + delete result; + return error; + } + + _imageResult = result; + return B_OK; } diff --git a/src/bin/debug/profile/BasicProfileResult.h b/src/bin/debug/profile/BasicProfileResult.h index e3cd38e54b..de159ca31a 100644 --- a/src/bin/debug/profile/BasicProfileResult.h +++ b/src/bin/debug/profile/BasicProfileResult.h @@ -16,7 +16,7 @@ public: image_id id); virtual ~BasicImageProfileResult(); - virtual status_t Init(); + status_t Init(); inline bool AddHit(addr_t address); inline void AddUnknownHit(); @@ -40,8 +40,9 @@ public: virtual void PrintResults( ImageProfileResultContainer* container); - virtual ImageProfileResult* CreateImageProfileResult(SharedImage* image, - image_id id); + virtual status_t GetImageProfileResult(SharedImage* image, + image_id id, + ImageProfileResult*& _imageResult); protected: int64 fTotalTicks; diff --git a/src/bin/debug/profile/CallgrindProfileResult.cpp b/src/bin/debug/profile/CallgrindProfileResult.cpp index e88f858389..eec6008f00 100644 --- a/src/bin/debug/profile/CallgrindProfileResult.cpp +++ b/src/bin/debug/profile/CallgrindProfileResult.cpp @@ -262,11 +262,23 @@ CallgrindProfileResult::PrintResults(ImageProfileResultContainer* container) } -ImageProfileResult* -CallgrindProfileResult::CreateImageProfileResult(SharedImage* image, - image_id id) +status_t +CallgrindProfileResult::GetImageProfileResult(SharedImage* image, image_id id, + ImageProfileResult*& _imageResult) { - return new(std::nothrow) CallgrindImageProfileResult(image, id); + CallgrindImageProfileResult* result + = new(std::nothrow) CallgrindImageProfileResult(image, id); + if (result == NULL) + return B_NO_MEMORY; + + status_t error = result->Init(); + if (error != B_OK) { + delete result; + return error; + } + + _imageResult = result; + return B_OK; } diff --git a/src/bin/debug/profile/CallgrindProfileResult.h b/src/bin/debug/profile/CallgrindProfileResult.h index 1517967e52..1407d0c82e 100644 --- a/src/bin/debug/profile/CallgrindProfileResult.h +++ b/src/bin/debug/profile/CallgrindProfileResult.h @@ -46,7 +46,7 @@ public: image_id id); virtual ~CallgrindImageProfileResult(); - virtual status_t Init(); + status_t Init(); inline void AddSymbolHit(int32 symbolIndex, CallgrindImageProfileResult* calledImage, @@ -74,8 +74,9 @@ public: virtual void PrintResults( ImageProfileResultContainer* container); - virtual ImageProfileResult* CreateImageProfileResult(SharedImage* image, - image_id id); + virtual status_t GetImageProfileResult(SharedImage* image, + image_id id, + ImageProfileResult*& _imageResult); private: void _PrintFunction(FILE* out, diff --git a/src/bin/debug/profile/Jamfile b/src/bin/debug/profile/Jamfile index 6b88df472a..e3f3fc2eee 100644 --- a/src/bin/debug/profile/Jamfile +++ b/src/bin/debug/profile/Jamfile @@ -16,6 +16,7 @@ BinCommand profile ProfiledEntity.cpp ProfileResult.cpp SharedImage.cpp + SummaryProfileResult.cpp Team.cpp Thread.cpp profile.cpp diff --git a/src/bin/debug/profile/Options.h b/src/bin/debug/profile/Options.h index b4cc6eaaae..eeaac04c21 100644 --- a/src/bin/debug/profile/Options.h +++ b/src/bin/debug/profile/Options.h @@ -23,7 +23,8 @@ struct Options { profile_loading(false), profile_teams(true), profile_threads(true), - analyze_full_stack(false) + analyze_full_stack(false), + summary_result(false) { } @@ -37,6 +38,7 @@ struct Options { bool profile_teams; bool profile_threads; bool analyze_full_stack; + bool summary_result; }; diff --git a/src/bin/debug/profile/ProfileResult.h b/src/bin/debug/profile/ProfileResult.h index 16b8be142e..11d497baa1 100644 --- a/src/bin/debug/profile/ProfileResult.h +++ b/src/bin/debug/profile/ProfileResult.h @@ -6,6 +6,8 @@ #define PROFILE_RESULT_H +#include + #include #include "SharedImage.h" @@ -15,7 +17,7 @@ class ProfiledEntity; class Team; -class ImageProfileResult { +class ImageProfileResult : public BReferenceable { public: ImageProfileResult(SharedImage* image, image_id id); @@ -58,13 +60,15 @@ public: }; -class ProfileResult { +class ProfileResult : public BReferenceable { public: ProfileResult(); virtual ~ProfileResult(); virtual status_t Init(ProfiledEntity* entity); + ProfiledEntity* Entity() const { return fEntity; } + void SetInterval(bigtime_t interval); virtual void AddSamples( @@ -75,8 +79,9 @@ public: virtual void PrintResults( ImageProfileResultContainer* container) = 0; - virtual ImageProfileResult* CreateImageProfileResult(SharedImage* image, - image_id id) = 0; + virtual status_t GetImageProfileResult(SharedImage* image, + image_id id, + ImageProfileResult*& _imageResult) = 0; protected: template diff --git a/src/bin/debug/profile/SummaryProfileResult.cpp b/src/bin/debug/profile/SummaryProfileResult.cpp new file mode 100644 index 0000000000..3dd4f0e5fa --- /dev/null +++ b/src/bin/debug/profile/SummaryProfileResult.cpp @@ -0,0 +1,148 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + + +#include "SummaryProfileResult.h" + +#include + + +// #pragma mark - SummaryImage + + +SummaryImage::SummaryImage(ImageProfileResult* result) + : + fResult(result) +{ + fResult->AcquireReference(); +} + + +SummaryImage::~SummaryImage() +{ + fResult->ReleaseReference(); +} + + +// #pragma mark - SummaryProfileResult + + +SummaryProfileResult::SummaryProfileResult(ProfileResult* result) + : + fResult(result), + fNextImageID(1) +{ + fResult->AcquireReference(); +} + + +SummaryProfileResult::~SummaryProfileResult() +{ + fResult->ReleaseReference(); +} + + +status_t +SummaryProfileResult::Init(ProfiledEntity* entity) +{ + status_t error = ProfileResult::Init(entity); + if (error != B_OK) + return error; + + error = fImages.Init(); + if (error != B_OK) + return error; + + return B_OK; +} + + +void +SummaryProfileResult::AddSamples(ImageProfileResultContainer* container, + addr_t* samples, int32 sampleCount) +{ + fResult->AddSamples(container, samples, sampleCount); +} + + +void +SummaryProfileResult::AddDroppedTicks(int32 dropped) +{ + fResult->AddDroppedTicks(dropped); +} + + +void +SummaryProfileResult::PrintResults(ImageProfileResultContainer* container) +{ + // This is called for individual threads. We only print results in + // PrintSummaryResults(), though. +} + + +void +SummaryProfileResult::PrintSummaryResults() +{ + fResult->PrintResults(this); +} + + +status_t +SummaryProfileResult::GetImageProfileResult(SharedImage* image, image_id id, + ImageProfileResult*& _imageResult) +{ + // Check whether we do already know the image. + SummaryImage* summaryImage = fImages.Lookup(image); + if (summaryImage == NULL) { + // nope, create it + ImageProfileResult* imageResult; + status_t error = fResult->GetImageProfileResult(image, fNextImageID++, + imageResult); + if (error != B_OK) + return error; + + BReference imageResultReference(imageResult, true); + + summaryImage = new(std::nothrow) SummaryImage(imageResult); + if (summaryImage == NULL) + return B_NO_MEMORY; + + fImages.Insert(summaryImage); + } + + _imageResult = summaryImage->Result(); + _imageResult->AcquireReference(); + + return B_OK; +} + + +int32 +SummaryProfileResult::CountImages() const +{ + return fImages.CountElements(); +} + + +ImageProfileResult* +SummaryProfileResult::VisitImages(Visitor& visitor) const +{ + for (ImageTable::Iterator it = fImages.GetIterator(); + SummaryImage* image = it.Next();) { + if (visitor.VisitImage(image->Result())) + return image->Result(); + } + + return NULL; +} + + +ImageProfileResult* +SummaryProfileResult::FindImage(addr_t address, addr_t& _loadDelta) const +{ + // We cannot and don't need to implement this. It's only relevant for + // AddSamples(), where we use the caller's container implementation. + return NULL; +} diff --git a/src/bin/debug/profile/SummaryProfileResult.h b/src/bin/debug/profile/SummaryProfileResult.h new file mode 100644 index 0000000000..1f85274d8a --- /dev/null +++ b/src/bin/debug/profile/SummaryProfileResult.h @@ -0,0 +1,94 @@ +/* + * Copyright 2010, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef SUMMARY_PROFILE_RESULT_H +#define SUMMARY_PROFILE_RESULT_H + + +#include + +#include "ProfileResult.h" + + +class SummaryImage { +public: + SummaryImage(ImageProfileResult* result); + ~SummaryImage(); + + ImageProfileResult* Result() const { return fResult; } + SharedImage* GetImage() const { return fResult->GetImage(); } + + SummaryImage*& HashNext() { return fHashNext; } + +private: + ImageProfileResult* fResult; + SummaryImage* fHashNext; +}; + + +struct SummaryImageHashDefinition { + typedef SharedImage* KeyType; + typedef SummaryImage ValueType; + + size_t HashKey(SharedImage* key) const + { + return (addr_t)key / (2 * sizeof(void*)); + } + + size_t Hash(SummaryImage* value) const + { + return HashKey(value->GetImage()); + } + + bool Compare(SharedImage* key, SummaryImage* value) const + { + return value->GetImage() == key; + } + + SummaryImage*& GetLink(SummaryImage* value) const + { + return value->HashNext(); + } +}; + + +class SummaryProfileResult : public ProfileResult, + private ImageProfileResultContainer { +public: + SummaryProfileResult(ProfileResult* result); + virtual ~SummaryProfileResult(); + + virtual status_t Init(ProfiledEntity* entity); + + virtual void AddSamples( + ImageProfileResultContainer* container, + addr_t* samples, int32 sampleCount); + virtual void AddDroppedTicks(int32 dropped); + virtual void PrintResults( + ImageProfileResultContainer* container); + + virtual status_t GetImageProfileResult(SharedImage* image, + image_id id, + ImageProfileResult*& _imageResult); + + void PrintSummaryResults(); + +private: + typedef BOpenHashTable ImageTable; + +private: + // ImageProfileResultContainer + virtual int32 CountImages() const; + virtual ImageProfileResult* VisitImages(Visitor& visitor) const; + virtual ImageProfileResult* FindImage(addr_t address, + addr_t& _loadDelta) const; + +private: + ProfileResult* fResult; + ImageTable fImages; + image_id fNextImageID; +}; + + +#endif // SUMMARY_PROFILE_RESULT_H diff --git a/src/bin/debug/profile/Thread.cpp b/src/bin/debug/profile/Thread.cpp index 75ad661e88..c82608177a 100644 --- a/src/bin/debug/profile/Thread.cpp +++ b/src/bin/debug/profile/Thread.cpp @@ -27,13 +27,14 @@ ThreadImage::ThreadImage(Image* image, ImageProfileResult* result) fResult(result) { fImage->AcquireReference(); + fResult->AcquireReference(); } ThreadImage::~ThreadImage() { fImage->ReleaseReference(); - delete fResult; + fResult->ReleaseReference(); } @@ -58,7 +59,8 @@ Thread::~Thread() if (fSampleArea >= 0) delete_area(fSampleArea); - delete fProfileResult; + if (fProfileResult != NULL) + fProfileResult->ReleaseReference(); while (ThreadImage* image = fImages.RemoveHead()) delete image; @@ -91,8 +93,14 @@ Thread::EntityType() const void Thread::SetProfileResult(ProfileResult* result) { - delete fProfileResult; + ProfileResult* oldResult = fProfileResult; + fProfileResult = result; + if (fProfileResult != NULL) + fProfileResult->AcquireReference(); + + if (oldResult) + oldResult->ReleaseReference(); } @@ -128,22 +136,17 @@ Thread::SetLazyImages(bool lazy) status_t Thread::AddImage(Image* image) { - ImageProfileResult* result = fProfileResult->CreateImageProfileResult( - image->GetSharedImage(), image->ID()); - if (result == NULL) - return B_NO_MEMORY; - - status_t error = result->Init(); - if (error != B_OK) { - delete result; + ImageProfileResult* result; + status_t error = fProfileResult->GetImageProfileResult( + image->GetSharedImage(), image->ID(), result); + if (error != B_OK) return error; - } + + BReference resultReference(result, true); ThreadImage* threadImage = new(std::nothrow) ThreadImage(image, result); - if (threadImage == NULL) { - delete result; + if (threadImage == NULL) return B_NO_MEMORY; - } if (fLazyImages) fNewImages.Add(threadImage); diff --git a/src/bin/debug/profile/profile.cpp b/src/bin/debug/profile/profile.cpp index 412f8b27db..4f169b6076 100644 --- a/src/bin/debug/profile/profile.cpp +++ b/src/bin/debug/profile/profile.cpp @@ -35,6 +35,7 @@ #include "debug_utils.h" #include "Image.h" #include "Options.h" +#include "SummaryProfileResult.h" #include "Team.h" @@ -84,6 +85,8 @@ static const char* kUsage = " caller stack per tick. If the topmost address doesn't\n" " hit a known image, the next address will be matched\n" " (and so on).\n" + " -S - Don't output results for individual threads, but\n" + " produce a combined output at the end.\n" " -v - Create valgrind/callgrind output. is the\n" " directory where to put the output files.\n" ; @@ -94,22 +97,46 @@ Options gOptions; static bool sCaughtDeadlySignal = false; -class ThreadManager { +class ThreadManager : private ProfiledEntity { public: ThreadManager(port_id debuggerPort) : fTeams(20, true), fThreads(20, true), fKernelTeam(NULL), - fDebuggerPort(debuggerPort) + fDebuggerPort(debuggerPort), + fSummaryProfileResult(NULL) { } - ~ThreadManager() + virtual ~ThreadManager() { // release image references for (ImageMap::iterator it = fImages.begin(); it != fImages.end(); ++it) it->second->RemoveReference(); + + if (fSummaryProfileResult != NULL) + fSummaryProfileResult->ReleaseReference(); + } + + status_t Init() + { + if (!gOptions.summary_result) + return B_OK; + + ProfileResult* profileResult; + status_t error = _CreateProfileResult(this, profileResult); + if (error != B_OK) + return error; + + BReference profileResultReference(profileResult, true); + + fSummaryProfileResult = new(std::nothrow) SummaryProfileResult( + profileResult); + if (fSummaryProfileResult == NULL) + return B_NO_MEMORY; + + return fSummaryProfileResult->Init(profileResult->Entity()); } status_t AddTeam(team_id teamID, Team** _team = NULL) @@ -145,7 +172,7 @@ public: if (thread == NULL) return B_NO_MEMORY; - status_t error = _CreateProfileResult(thread); + status_t error = _CreateThreadProfileResult(thread); if (error != B_OK) { delete thread; return error; @@ -244,6 +271,28 @@ public: } } + void PrintSummaryResults() + { + if (fSummaryProfileResult != NULL) + fSummaryProfileResult->PrintSummaryResults(); + } + +private: + virtual int32 EntityID() const + { + return 1; + } + + virtual const char* EntityName() const + { + return "all"; + } + + virtual const char* EntityType() const + { + return "summary"; + } + private: status_t _AddTeam(team_id teamID, system_profiler_team_added* addedInfo, Team** _team = NULL) @@ -333,9 +382,28 @@ private: return B_OK; } - status_t _CreateProfileResult(Thread* thread) + status_t _CreateThreadProfileResult(Thread* thread) + { + if (fSummaryProfileResult != NULL) { + thread->SetProfileResult(fSummaryProfileResult); + return B_OK; + } + + ProfileResult* profileResult; + status_t error = _CreateProfileResult(thread, profileResult); + if (error != B_OK) + return error; + + thread->SetProfileResult(profileResult); + + return B_OK; + } + + status_t _CreateProfileResult(ProfiledEntity* profiledEntity, + ProfileResult*& _profileResult) { ProfileResult* profileResult; + if (gOptions.callgrind_directory != NULL) profileResult = new(std::nothrow) CallgrindProfileResult; else if (gOptions.analyze_full_stack) @@ -346,13 +414,13 @@ private: if (profileResult == NULL) return B_NO_MEMORY; - status_t error = profileResult->Init(thread); - if (error != B_OK) { - delete profileResult; - return error; - } + BReference profileResultReference(profileResult, true); - thread->SetProfileResult(profileResult); + status_t error = profileResult->Init(profiledEntity); + if (error != B_OK) + return error; + + _profileResult = profileResultReference.Detach(); return B_OK; } @@ -412,6 +480,7 @@ private: ImageMap fImages; Team* fKernelTeam; port_id fDebuggerPort; + SummaryProfileResult* fSummaryProfileResult; }; @@ -608,6 +677,12 @@ profile_all(const char* const* programArgs, int programArgCount) // create a thread manager ThreadManager threadManager(-1); // TODO: We don't need a debugger port! + status_t error = threadManager.Init(); + if (error != B_OK) { + fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName, + strerror(error)); + exit(1); + } // start profiling system_profiler_parameters profilerParameters; @@ -618,7 +693,7 @@ profile_all(const char* const* programArgs, int programArgCount) profilerParameters.interval = gOptions.interval; profilerParameters.stack_depth = gOptions.stack_depth; - status_t error = _kern_system_profiler_start(&profilerParameters); + error = _kern_system_profiler_start(&profilerParameters); if (error != B_OK) { fprintf(stderr, "%s: Failed to start profiling: %s\n", kCommandName, strerror(error)); @@ -678,6 +753,8 @@ profile_all(const char* const* programArgs, int programArgCount) Thread* thread = threadManager.ThreadAt(i); thread->PrintResults(); } + + threadManager.PrintSummaryResults(); } @@ -686,10 +763,10 @@ dump_recorded() { // retrieve recorded samples and parameters system_profiler_parameters profilerParameters; - status_t status = _kern_system_profiler_recorded(&profilerParameters); - if (status != B_OK) { + status_t error = _kern_system_profiler_recorded(&profilerParameters); + if (error != B_OK) { fprintf(stderr, "%s: Failed to get recorded profiling buffer: %s\n", - kCommandName, strerror(status)); + kCommandName, strerror(error)); exit(1); } @@ -699,10 +776,10 @@ dump_recorded() // create an area for the sample buffer area_info info; - status = get_area_info(profilerParameters.buffer_area, &info); - if (status != B_OK) { + error = get_area_info(profilerParameters.buffer_area, &info); + if (error != B_OK) { fprintf(stderr, "%s: Recorded profiling buffer invalid: %s\n", - kCommandName, strerror(status)); + kCommandName, strerror(error)); exit(1); } @@ -714,6 +791,12 @@ dump_recorded() // create a thread manager ThreadManager threadManager(-1); // TODO: We don't need a debugger port! + error = threadManager.Init(); + if (error != B_OK) { + fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName, + strerror(error)); + exit(1); + } // get the current buffer size_t bufferStart = bufferHeader->start; @@ -736,6 +819,8 @@ dump_recorded() Thread* thread = threadManager.ThreadAt(i); thread->PrintResults(); } + + threadManager.PrintSummaryResults(); } @@ -771,6 +856,13 @@ profile_single(const char* const* programArgs, int programArgCount) // add team and thread to the thread manager ThreadManager threadManager(debuggerPort); + error = threadManager.Init(); + if (error != B_OK) { + fprintf(stderr, "%s: Failed to init thread manager: %s\n", kCommandName, + strerror(error)); + exit(1); + } + if (threadManager.AddTeam(teamID) != B_OK || threadManager.AddThread(threadID) != B_OK) { exit(1); @@ -882,6 +974,9 @@ profile_single(const char* const* programArgs, int programArgCount) if (message.origin.thread >= 0 && message.origin.nub_port >= 0) continue_thread(message.origin.nub_port, message.origin.thread); } + + // prints summary results + threadManager.PrintSummaryResults(); } @@ -901,7 +996,7 @@ main(int argc, const char* const* argv) }; opterr = 0; // don't print errors - int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:rs:v:", + int c = getopt_long(argc, (char**)argv, "+acCfhi:klo:rsS:v:", sLongOptions, NULL); if (c == -1) break; @@ -941,6 +1036,9 @@ main(int argc, const char* const* argv) case 's': stackDepth = atol(optarg); break; + case 'S': + gOptions.summary_result = true; + break; case 'v': gOptions.callgrind_directory = optarg; gOptions.analyze_full_stack = true;