diff --git a/src/bin/debug/profile/CallgrindThreadProfileResult.cpp b/src/bin/debug/profile/CallgrindThreadProfileResult.cpp new file mode 100644 index 0000000000..0b4e72feec --- /dev/null +++ b/src/bin/debug/profile/CallgrindThreadProfileResult.cpp @@ -0,0 +1,277 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ + +#include "CallgrindThreadProfileResult.h" + +#include +#include + +#include +#include + +#include "Options.h" + + +// #pragma mark - CallgrindThreadImage + + +CallgrindThreadImage::CallgrindThreadImage(Image* image) + : + ThreadImage(image), + fFunctions(NULL), + fOutputIndex(0) +{ +} + + +CallgrindThreadImage::~CallgrindThreadImage() +{ + int32 symbolCount = fImage->SymbolCount(); + for (int32 i = 0; i < symbolCount; i++) { + while (CallgrindCalledFunction* calledFunction + = fFunctions[i].calledFunctions) { + fFunctions[i].calledFunctions = calledFunction->next; + delete calledFunction; + } + } + + delete[] fFunctions; +} + + +status_t +CallgrindThreadImage::Init() +{ + int32 symbolCount = fImage->SymbolCount(); + fFunctions = new(std::nothrow) CallgrindFunction[symbolCount]; + if (fFunctions == NULL) + return B_NO_MEMORY; + + memset(fFunctions, 0, sizeof(CallgrindFunction) * symbolCount); + + return B_OK; +} + + +void +CallgrindThreadImage::AddSymbolHit(int32 symbolIndex, + CallgrindThreadImage* calledImage, int32 calledSymbol) + +{ + fTotalHits++; + + CallgrindFunction& function = fFunctions[symbolIndex]; + if (calledImage != NULL) { + // check whether the called function is known already + CallgrindCalledFunction* calledFunction = function.calledFunctions; + while (calledFunction != NULL) { + if (calledFunction->image == calledImage + && calledFunction->function == calledSymbol) { + break; + } + calledFunction = calledFunction->next; + } + + // create a new CallgrindCalledFunction object, if not known + if (calledFunction == NULL) { + calledFunction = new(std::nothrow) CallgrindCalledFunction( + calledImage, calledSymbol); + if (calledFunction == NULL) + return; + + calledFunction->next = function.calledFunctions; + function.calledFunctions = calledFunction; + } + + calledFunction->hits++; + } else + function.hits++; +} + + +CallgrindFunction* +CallgrindThreadImage::Functions() const +{ + return fFunctions; +} + + +int32 +CallgrindThreadImage::OutputIndex() const +{ + return fOutputIndex; +} + + +void +CallgrindThreadImage::SetOutputIndex(int32 index) +{ + fOutputIndex = index; +} + + +// #pragma mark - CallgrindThreadProfileResult + + +CallgrindThreadProfileResult::CallgrindThreadProfileResult() + : + fTotalTicks(0), + fUnkownTicks(0), + fDroppedTicks(0), + fNextImageOutputIndex(1), + fNextFunctionOutputIndex(1) +{ +} + + +void +CallgrindThreadProfileResult::AddSamples(addr_t* samples, int32 sampleCount) +{ + int32 unknownSamples = 0; + CallgrindThreadImage* previousImage = NULL; + int32 previousSymbol = -1; + + // TODO: That probably doesn't work with recursive functions. + for (int32 i = 0; i < sampleCount; i++) { + addr_t address = samples[i]; + CallgrindThreadImage* image = FindImage(address); + int32 symbol = -1; + if (image != NULL) { + symbol = image->GetImage()->FindSymbol(address); + if (symbol >= 0) { + image->AddSymbolHit(symbol, previousImage, previousSymbol); + previousImage = image; + previousSymbol = symbol; + } + } else + unknownSamples++; + } + + if (unknownSamples == sampleCount) + fUnkownTicks++; + + fTotalTicks++; +} + + +void +CallgrindThreadProfileResult::AddDroppedTicks(int32 dropped) +{ + fDroppedTicks += dropped; +} + + +void +CallgrindThreadProfileResult::PrintResults() +{ + // create output file + mkdir(gOptions.callgrind_directory, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + char fileName[B_PATH_NAME_LENGTH]; + snprintf(fileName, sizeof(fileName), "%s/callgrind.out.%ld", + gOptions.callgrind_directory, fThread->ID()); + + FILE* out = fopen(fileName, "w+"); + if (out == NULL) { + fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n", + kCommandName, fileName, strerror(errno)); + return; + } + + // write the header + fprintf(out, "version: 1\n"); + fprintf(out, "creator: Haiku profile\n"); + fprintf(out, "pid: %ld\n", fThread->ID()); + fprintf(out, "cmd: %s\n", fThread->Name()); + fprintf(out, "part: 1\n\n"); + + fprintf(out, "positions: line\n"); + fprintf(out, "events: Ticks Time\n"); + fprintf(out, "summary: %lld\n", fTotalTicks); + + // get hit images + CallgrindThreadImage* images[fOldImages.Size() + fImages.Size()]; + int32 imageCount = GetHitImages(images); + + for (int32 i = 0; i < imageCount; i++) { + CallgrindThreadImage* image = images[i]; + + CallgrindFunction* functions = image->Functions(); + int32 imageSymbolCount = image->GetImage()->SymbolCount(); + for (int32 k = 0; k < imageSymbolCount; k++) { + CallgrindFunction& function = functions[k]; + if (function.hits == 0 && function.calledFunctions == NULL) + continue; + + fprintf(out, "\n"); + _PrintFunction(out, image, k, false); + fprintf(out, "0 %lld %lld\n", function.hits, + function.hits * fInterval); + + CallgrindCalledFunction* calledFunction = function.calledFunctions; + while (calledFunction != NULL) { + _PrintFunction(out, calledFunction->image, + calledFunction->function, true); + fprintf(out, "calls=%lld 0\n", calledFunction->hits); + fprintf(out, "0 %lld %lld\n", calledFunction->hits, + calledFunction->hits * fInterval); + calledFunction = calledFunction->next; + } + } + } + + // print pseudo-functions for unknown and dropped ticks + if (fUnkownTicks + fDroppedTicks > 0) { + fprintf(out, "\nob=\n"); + + if (fUnkownTicks > 0) { + fprintf(out, "\nfn=unknown\n"); + fprintf(out, "0 %lld\n", fUnkownTicks); + } + + if (fDroppedTicks > 0) { + fprintf(out, "\nfn=dropped\n"); + fprintf(out, "0 %lld\n", fDroppedTicks); + } + } + + fclose(out); +} + + +CallgrindThreadImage* +CallgrindThreadProfileResult::CreateThreadImage(Image* image) +{ + return new(std::nothrow) CallgrindThreadImage(image); +} + + +void +CallgrindThreadProfileResult::_PrintFunction(FILE* out, + CallgrindThreadImage* image, int32 functionIndex, bool called) +{ + if (image->OutputIndex() == 0) { + // need to print the image name + int32 index = fNextImageOutputIndex++; + image->SetOutputIndex(index); + fprintf(out, "%sob=(%ld) %s:%ld\n", called ? "c" : "", index, + image->GetImage()->Info().name, image->ID()); + } else { + // image is already known + // TODO: We may not need to print it at all! + fprintf(out, "%sob=(%ld)\n", called ? "c" : "", image->OutputIndex()); + } + + CallgrindFunction& function = image->Functions()[functionIndex]; + if (function.outputIndex == 0) { + // need to print the function name + function.outputIndex = fNextFunctionOutputIndex++; + fprintf(out, "%sfn=(%ld) %s\n", called ? "c" : "", function.outputIndex, + image->GetImage()->Symbols()[functionIndex]->Name()); + } else { + // function is already known + fprintf(out, "%sfn=(%ld)\n", called ? "c" : "", function.outputIndex); + } +} diff --git a/src/bin/debug/profile/CallgrindThreadProfileResult.h b/src/bin/debug/profile/CallgrindThreadProfileResult.h new file mode 100644 index 0000000000..74f28cbc0c --- /dev/null +++ b/src/bin/debug/profile/CallgrindThreadProfileResult.h @@ -0,0 +1,89 @@ +/* + * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. + * Distributed under the terms of the MIT License. + */ +#ifndef CALLGRIND_THREAD_PROFILE_RESULT_H +#define CALLGRIND_THREAD_PROFILE_RESULT_H + +#include + +#include "Thread.h" + + +class CallgrindThreadImage; + + +struct CallgrindCalledFunction { + CallgrindCalledFunction* next; + CallgrindThreadImage* image; + int32 function; + int64 hits; + + CallgrindCalledFunction(CallgrindThreadImage* image, int32 function) + : + next(NULL), + image(image), + function(function), + hits(0) + { + } +}; + + +struct CallgrindFunction { + int64 hits; + CallgrindCalledFunction* calledFunctions; + int32 outputIndex; + // index when generating the output file +}; + + +class CallgrindThreadImage : public ThreadImage, + public DoublyLinkedListLinkImpl { +public: + CallgrindThreadImage(Image* image); + virtual ~CallgrindThreadImage(); + + virtual status_t Init(); + + inline void AddSymbolHit(int32 symbolIndex, + CallgrindThreadImage* calledImage, + int32 calledSymbol); + + inline CallgrindFunction* Functions() const; + + inline int32 OutputIndex() const; + inline void SetOutputIndex(int32 index); + +private: + CallgrindFunction* fFunctions; + int32 fOutputIndex; +}; + + +class CallgrindThreadProfileResult + : public AbstractThreadProfileResult { +public: + CallgrindThreadProfileResult(); + + virtual void AddSamples(addr_t* samples, + int32 sampleCount); + virtual void AddDroppedTicks(int32 dropped); + virtual void PrintResults(); + + virtual CallgrindThreadImage* CreateThreadImage(Image* image); + +private: + void _PrintFunction(FILE* out, + CallgrindThreadImage* image, + int32 functionIndex, bool called); +private: + int64 fTotalTicks; + int64 fUnkownTicks; + int64 fDroppedTicks; + int32 fNextImageOutputIndex; + int32 fNextFunctionOutputIndex; +}; + + +#endif // CALLGRIND_THREAD_PROFILE_RESULT_H diff --git a/src/bin/debug/profile/Jamfile b/src/bin/debug/profile/Jamfile index fdf2d5b017..c5bb6f1aec 100644 --- a/src/bin/debug/profile/Jamfile +++ b/src/bin/debug/profile/Jamfile @@ -11,6 +11,7 @@ SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ; BinCommand profile : BasicThreadProfileResult.cpp + CallgrindThreadProfileResult.cpp Image.cpp Team.cpp Thread.cpp diff --git a/src/bin/debug/profile/Options.h b/src/bin/debug/profile/Options.h index 743c26c983..db2dcc9dae 100644 --- a/src/bin/debug/profile/Options.h +++ b/src/bin/debug/profile/Options.h @@ -16,6 +16,7 @@ struct Options { interval(1000), stack_depth(5), output(NULL), + callgrind_directory(NULL), profile_kernel(true), profile_loading(false), profile_teams(true), @@ -27,6 +28,7 @@ struct Options { bigtime_t interval; int32 stack_depth; FILE* output; + const char* callgrind_directory; bool profile_kernel; bool profile_loading; bool profile_teams; diff --git a/src/bin/debug/profile/profile.cpp b/src/bin/debug/profile/profile.cpp index c5bc344dc2..f56d30425f 100644 --- a/src/bin/debug/profile/profile.cpp +++ b/src/bin/debug/profile/profile.cpp @@ -24,6 +24,7 @@ #include #include "BasicThreadProfileResult.h" +#include "CallgrindThreadProfileResult.h" #include "debug_utils.h" #include "Image.h" #include "Options.h" @@ -67,6 +68,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" + " -v - Create valgrind/callgrind output. is the\n" + " directory where to put the output files.\n" ; @@ -176,7 +179,9 @@ private: status_t _CreateThreadProfileResult(Thread* thread) { ThreadProfileResult* profileResult; - if (gOptions.analyze_full_stack) + if (gOptions.callgrind_directory != NULL) + profileResult = new(std::nothrow) CallgrindThreadProfileResult; + else if (gOptions.analyze_full_stack) profileResult = new(std::nothrow) InclusiveThreadProfileResult; else profileResult = new(std::nothrow) ExclusiveThreadProfileResult; @@ -239,7 +244,7 @@ main(int argc, const char* const* argv) }; opterr = 0; // don't print errors - int c = getopt_long(argc, (char**)argv, "+cCfhi:klo:s:", sLongOptions, + int c = getopt_long(argc, (char**)argv, "+cCfhi:klo:s:v:", sLongOptions, NULL); if (c == -1) break; @@ -273,6 +278,11 @@ main(int argc, const char* const* argv) case 's': stackDepth = atol(optarg); break; + case 'v': + gOptions.callgrind_directory = optarg; + gOptions.analyze_full_stack = true; + gOptions.stack_depth = 64; + break; default: print_usage_and_exit(true); break;