⛏️ index : haiku.git

/*
 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Copyright 2013, Rene Gollent, rene@gollent.com.
 * Distributed under the terms of the MIT License.
 */

#include "Team.h"

#include <new>

#include <image.h>

#include <debug_support.h>
#include <system_profiler_defs.h>

#include "debug_utils.h"

#include "Image.h"
#include "Options.h"


//#define TRACE_PROFILE_TEAM
#ifdef TRACE_PROFILE_TEAM
#	define TRACE(x...) printf(x)
#else
#	define TRACE(x...) do {} while(false)
#endif


enum {
	SAMPLE_AREA_SIZE	= 128 * 1024,
};


Team::Team()
	:
	fID(-1),
	fNubPort(-1),
	fThreads(),
	fImages(20)
{
	fDebugContext.nub_port = -1;
}


Team::~Team()
{
	if (fDebugContext.nub_port >= 0)
		destroy_debug_context(&fDebugContext);

	if (fNubPort >= 0)
		remove_team_debugger(fID);

	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++)
		image->ReleaseReference();
}


status_t
Team::Init(team_id teamID, port_id debuggerPort)
{
	// get team info
	team_info teamInfo;
	status_t error = get_team_info(teamID, &teamInfo);
	if (error != B_OK)
		return error;

	fID = teamID;
	fArgs = teamInfo.args;

	// install ourselves as the team debugger
	fNubPort = install_team_debugger(teamID, debuggerPort);
	if (fNubPort < 0) {
		fprintf(stderr,
			"%s: Failed to install as debugger for team %" B_PRId32 ": "
			"%s\n", kCommandName, teamID, strerror(fNubPort));
		return fNubPort;
	}

	// init debug context
	error = init_debug_context(&fDebugContext, teamID, fNubPort);
	if (error != B_OK) {
		fprintf(stderr,
			"%s: Failed to init debug context for team %" B_PRId32 ": "
			"%s\n", kCommandName, teamID, strerror(error));
		return error;
	}

	// set team debugging flags
	int32 teamDebugFlags = B_TEAM_DEBUG_THREADS
		| B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES
		| B_TEAM_DEBUG_STOP_NEW_THREADS;
	error = set_team_debugging_flags(fNubPort, teamDebugFlags);
	if (error != B_OK)
		return error;

	return B_OK;
}


status_t
Team::Init(system_profiler_team_added* addedInfo)
{
	fID = addedInfo->team;
	fArgs = addedInfo->name + addedInfo->args_offset;
	return B_OK;
}


status_t
Team::InitThread(Thread* thread)
{
	// The thread
	thread->SetLazyImages(!_SynchronousProfiling());

	// create the sample area
	char areaName[B_OS_NAME_LENGTH];
	snprintf(areaName, sizeof(areaName), "profiling samples %" B_PRId32,
		thread->ID());
	void* samples;
	area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
		SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
	if (sampleArea < 0) {
		fprintf(stderr,
			"%s: Failed to create sample area for thread %" B_PRId32 ": "
			"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
		return sampleArea;
	}

	thread->SetSampleArea(sampleArea, (addr_t*)samples);

	// add the current images to the thread
	int32 imageCount = fImages.CountItems();
	for (int32 i = 0; i < imageCount; i++) {
		status_t error = thread->AddImage(fImages.ItemAt(i));
		if (error != B_OK)
			return error;
	}

	if (!_SynchronousProfiling()) {
		// start profiling
		debug_nub_start_profiler message;
		message.reply_port = fDebugContext.reply_port;
		message.thread = thread->ID();
		message.interval = gOptions.interval;
		message.sample_area = sampleArea;
		message.stack_depth = gOptions.stack_depth;
		message.variable_stack_depth = gOptions.analyze_full_stack;
		message.profile_kernel = gOptions.profile_kernel;

		debug_nub_start_profiler_reply reply;
		status_t error = send_debug_message(&fDebugContext,
			B_DEBUG_MESSAGE_START_PROFILER, &message, sizeof(message), &reply,
			sizeof(reply));
		if (error != B_OK || (error = reply.error) != B_OK) {
			fprintf(stderr,
				"%s: Failed to start profiler for thread %" B_PRId32 ": %s\n",
				kCommandName, thread->ID(), strerror(error));
			return error;
		}

		thread->SetInterval(reply.interval);

		fThreads.Add(thread);

		// resume the target thread to be sure it's running
		continue_thread(fDebugContext.nub_port, thread->ID());
	} else {
		// debugger-less profiling
		thread->SetInterval(gOptions.interval);
		fThreads.Add(thread);
	}

	return B_OK;
}


void
Team::RemoveThread(Thread* thread)
{
	fThreads.Remove(thread);
}


void
Team::Exec(int32 event, const char* args, const char* threadName)
{
	// remove all non-kernel images
	int32 imageCount = fImages.CountItems();
	for (int32 i = imageCount - 1; i >= 0; i--) {
		Image* image = fImages.ItemAt(i);
		if (image->Owner() == ID())
			_RemoveImage(i, event);
	}

	fArgs = args;

	// update the main thread
	ThreadList::Iterator it = fThreads.GetIterator();
	while (Thread* thread = it.Next()) {
		if (thread->ID() == ID()) {
			thread->UpdateInfo(threadName);
			break;
		}
	}
}


status_t
Team::AddImage(SharedImage* sharedImage, const image_info& imageInfo,
	team_id owner, int32 event)
{
	// create the image
	Image* image = new(std::nothrow) Image(sharedImage, imageInfo, owner,
		event);
	if (image == NULL)
		return B_NO_MEMORY;

	if (!fImages.AddItem(image)) {
		delete image;
		return B_NO_MEMORY;
	}

	// Although we generally synchronize the threads' images lazily, we have
	// to add new images at least, since otherwise images could be added
	// and removed again, and the hits inbetween could never be matched.
	ThreadList::Iterator it = fThreads.GetIterator();
	while (Thread* thread = it.Next())
		thread->AddImage(image);

	return B_OK;
}


status_t
Team::RemoveImage(image_id imageID, int32 event)
{
	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
		if (image->ID() == imageID) {
			_RemoveImage(i, event);
			return B_OK;
		}
	}

	return B_ENTRY_NOT_FOUND;
}


Image*
Team::FindImage(image_id id) const
{
	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
		if (image->ID() == id)
			return image;
	}

	return NULL;
}


void
Team::_RemoveImage(int32 index, int32 event)
{
	Image* image = fImages.RemoveItemAt(index);
	if (image == NULL)
		return;

	if (_SynchronousProfiling()) {
		ThreadList::Iterator it = fThreads.GetIterator();
		while (Thread* thread = it.Next())
			thread->RemoveImage(image);
	} else {
		// Note: We don't tell the threads that the image has been removed. They
		// will be updated lazily when their next profiler update arrives. This
		// is necessary, since the update might contain samples hitting that
		// image.
	}

	image->SetDeletionEvent(event);
	image->ReleaseReference();
}