Added option "-v <directory>". The tool will generate output files in
valgrind's callgrind format in the given directory. Those can be analyzed with graphical tools like KCachegrind. Recursive functions are probably not handled correctly yet. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27804 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
611c6ed977
commit
9aabd0ab3a
277
src/bin/debug/profile/CallgrindThreadProfileResult.cpp
Normal file
277
src/bin/debug/profile/CallgrindThreadProfileResult.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include "CallgrindThreadProfileResult.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
|
||||
#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=<pseudo>\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);
|
||||
}
|
||||
}
|
89
src/bin/debug/profile/CallgrindThreadProfileResult.h
Normal file
89
src/bin/debug/profile/CallgrindThreadProfileResult.h
Normal file
@ -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 <stdio.h>
|
||||
|
||||
#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<CallgrindThreadImage> {
|
||||
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<CallgrindThreadImage> {
|
||||
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
|
@ -11,6 +11,7 @@ SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||
BinCommand profile
|
||||
:
|
||||
BasicThreadProfileResult.cpp
|
||||
CallgrindThreadProfileResult.cpp
|
||||
Image.cpp
|
||||
Team.cpp
|
||||
Thread.cpp
|
||||
|
@ -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;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
#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 <directory> - Create valgrind/callgrind output. <directory> 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;
|
||||
|
Loading…
Reference in New Issue
Block a user