/*
 * 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 "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),
	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::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.%ld.%s.%lldms",
		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: %ld\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: %lld %lld\n", fTotalTicks, fTotalTicks * fInterval);
 
	// get hit images
	CallgrindImageProfileResult* 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 %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);
		}
	}
 
	fprintf(out, "\ntotals: %lld %lld\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);
		fprintf(out, "%sob=(%ld) %s:%ld\n", called ? "c" : "", index,
			image->GetImage()->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);
	}
}

V576 Incorrect format. Consider checking the third actual argument of the 'fprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'fprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the sixth actual argument of the 'fprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fifth actual argument of the 'snprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'fprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'fprintf' function. The memsize type argument is expected.

V576 Incorrect format. Consider checking the fourth actual argument of the 'fprintf' function. The memsize type argument is expected.