⛏️ index : haiku.git

/*
 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */


#include "CallgrindProfileResult.h"

#include <errno.h>
#include <sys/stat.h>

#include <algorithm>
#include <new>
#include <StackOrHeapArray.h>

#include "Options.h"
#include "ProfiledEntity.h"


// #pragma mark - CallgrindImageProfileResult


CallgrindImageProfileResult::CallgrindImageProfileResult(SharedImage* image,
	image_id id)
	:
	ImageProfileResult(image, id),
	fFunctions(NULL),
	fOutputIndex(0)
{
}


CallgrindImageProfileResult::~CallgrindImageProfileResult()
{
	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
CallgrindImageProfileResult::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
CallgrindImageProfileResult::AddSymbolHit(int32 symbolIndex,
	CallgrindImageProfileResult* 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*
CallgrindImageProfileResult::Functions() const
{
	return fFunctions;
}


int32
CallgrindImageProfileResult::OutputIndex() const
{
	return fOutputIndex;
}


void
CallgrindImageProfileResult::SetOutputIndex(int32 index)
{
	fOutputIndex = index;
}


// #pragma mark - CallgrindProfileResult


CallgrindProfileResult::CallgrindProfileResult()
	:
	fTotalTicks(0),
	fUnkownTicks(0),
	fExpectedTicks(0),
	fDroppedTicks(0),
	fNextImageOutputIndex(1),
	fNextFunctionOutputIndex(1)
{
}


void
CallgrindProfileResult::AddSamples(ImageProfileResultContainer* container,
	addr_t* samples, int32 sampleCount)
{
	int32 unknownSamples = 0;
	CallgrindImageProfileResult* 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];
		addr_t loadDelta;
		CallgrindImageProfileResult* image
			= static_cast<CallgrindImageProfileResult*>(
				container->FindImage(address, loadDelta));
		int32 symbol = -1;
		if (image != NULL) {
			symbol = image->GetImage()->FindSymbol(address - loadDelta);
			if (symbol >= 0) {
				image->AddSymbolHit(symbol, previousImage, previousSymbol);
				previousImage = image;
				previousSymbol = symbol;
			}
		} else
			unknownSamples++;
	}

	if (unknownSamples == sampleCount)
		fUnkownTicks++;

	fTotalTicks++;
}


void
CallgrindProfileResult::AddExpectedTicks(int32 expected)
{
	fExpectedTicks += expected;
}


void
CallgrindProfileResult::AddDroppedTicks(int32 dropped)
{
	fDroppedTicks += dropped;
}


void
CallgrindProfileResult::PrintResults(ImageProfileResultContainer* container)
{
	// create output file

	// create output dir
	mkdir(gOptions.callgrind_directory, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);

	// get the entity name and replace slashes by hyphens
	char entityName[B_OS_NAME_LENGTH];
	strlcpy(entityName, fEntity->EntityName(), sizeof(entityName));
	char* slash = entityName;
	while ((slash = strchr(slash, '/')) != NULL)
		*slash = '-';

	// create the file name
	char fileName[B_PATH_NAME_LENGTH];
	snprintf(fileName, sizeof(fileName),
		"%s/callgrind.out.%" B_PRId32 ".%s.%" B_PRId64 "ms",
		gOptions.callgrind_directory, fEntity->EntityID(), entityName,
		fTotalTicks * fInterval);

	// create the file
	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: %" B_PRId32 "\n", fEntity->EntityID());
	fprintf(out, "cmd: %s\n", fEntity->EntityName());
	fprintf(out, "part: 1\n\n");

	fprintf(out, "positions: line\n");
	fprintf(out, "events: Ticks Time\n");
	fprintf(out, "summary: %" B_PRId64 " %" B_PRId64 "\n",
		fTotalTicks, fTotalTicks * fInterval);

	// get hit images
	BStackOrHeapArray<CallgrindImageProfileResult*, 128> images(container->CountImages());
	int32 imageCount = GetHitImages(container, &*images);

	for (int32 i = 0; i < imageCount; i++) {
		CallgrindImageProfileResult* 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 %" B_PRId64 " %" B_PRId64 "\n", function.hits,
				function.hits * fInterval);

			CallgrindCalledFunction* calledFunction = function.calledFunctions;
			while (calledFunction != NULL) {
				_PrintFunction(out, calledFunction->image,
					calledFunction->function, true);
				fprintf(out, "calls=%" B_PRId64 " 0\n", calledFunction->hits);
				fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\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 %" B_PRId64 "\n", fUnkownTicks);
		}

		if (fDroppedTicks > 0) {
			fprintf(out, "\nfn=dropped\n");
			fprintf(out, "0 %" B_PRId64 "\n", fDroppedTicks);
		}
	}

	fprintf(out, "\ntotals: %" B_PRId64 " %" B_PRId64 "\n",
		fTotalTicks, fTotalTicks * fInterval);

	fclose(out);
}


status_t
CallgrindProfileResult::GetImageProfileResult(SharedImage* image, image_id id,
	ImageProfileResult*& _imageResult)
{
	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;
}


void
CallgrindProfileResult::_PrintFunction(FILE* out,
	CallgrindImageProfileResult* image, int32 functionIndex, bool called)
{
	if (image->OutputIndex() == 0) {
		// need to print the image name
		int32 index = fNextImageOutputIndex++;
		image->SetOutputIndex(index);
		const char* name = image->GetImage()->Name();
		if (name[0] == '/' || strcmp(name, "commpage") == 0) {
			fprintf(out,
				"%sob=(%" B_PRId32 ") %s\n", called ? "c" : "",
				index, name);
		} else {
			// add ID to image name
			fprintf(out,
				"%sob=(%" B_PRId32 ") %s:%" B_PRId32 "\n", called ? "c" : "",
				index, name, image->ID());
		}
	} else {
		// image is already known
		// TODO: We may not need to print it at all!
		fprintf(out,
			"%sob=(%" B_PRId32 ")\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=(%" B_PRId32 ") %s\n", called ? "c" : "",
			function.outputIndex,
			image->GetImage()->Symbols()[functionIndex]->Name());
	} else {
		// function is already known
		fprintf(out,
			"%sfn=(%" B_PRId32 ")\n", called ? "c" : "", function.outputIndex);
	}
}