1/*
2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2013, Rene Gollent, rene@gollent.com.
4 * Distributed under the terms of the MIT License.
5 */
6
7#include "Team.h"
8
9#include <new>
10
11#include <image.h>
12
13#include <debug_support.h>
14#include <system_profiler_defs.h>
15
16#include "debug_utils.h"
17
18#include "Image.h"
19#include "Options.h"
20
21
22//#define TRACE_PROFILE_TEAM
23#ifdef TRACE_PROFILE_TEAM
24#	define TRACE(x...) printf(x)
25#else
26#	define TRACE(x...) do {} while(false)
27#endif
28
29
30enum {
31	SAMPLE_AREA_SIZE	= 128 * 1024,
32};
33
34
35Team::Team()
36	:
37	fID(-1),
38	fNubPort(-1),
39	fThreads(),
40	fImages(20, false)
41{
42	fDebugContext.nub_port = -1;
43}
44
45
46Team::~Team()
47{
48	if (fDebugContext.nub_port >= 0)
49		destroy_debug_context(&fDebugContext);
50
51	if (fNubPort >= 0)
52		remove_team_debugger(fID);
53
54	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++)
55		image->ReleaseReference();
56}
57
58
59status_t
60Team::Init(team_id teamID, port_id debuggerPort)
61{
62	// get team info
63	team_info teamInfo;
64	status_t error = get_team_info(teamID, &teamInfo);
65	if (error != B_OK)
66		return error;
67
68	fID = teamID;
69	fArgs = teamInfo.args;
70
71	// install ourselves as the team debugger
72	fNubPort = install_team_debugger(teamID, debuggerPort);
73	if (fNubPort < 0) {
74		fprintf(stderr,
75			"%s: Failed to install as debugger for team %" B_PRId32 ": "
76			"%s\n", kCommandName, teamID, strerror(fNubPort));
77		return fNubPort;
78	}
79
80	// init debug context
81	error = init_debug_context(&fDebugContext, teamID, fNubPort);
82	if (error != B_OK) {
83		fprintf(stderr,
84			"%s: Failed to init debug context for team %" B_PRId32 ": "
85			"%s\n", kCommandName, teamID, strerror(error));
86		return error;
87	}
88
89	// set team debugging flags
90	int32 teamDebugFlags = B_TEAM_DEBUG_THREADS
91		| B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES;
92	error = set_team_debugging_flags(fNubPort, teamDebugFlags);
93	if (error != B_OK)
94		return error;
95
96	return B_OK;
97}
98
99
100status_t
101Team::Init(system_profiler_team_added* addedInfo)
102{
103	fID = addedInfo->team;
104	fArgs = addedInfo->name + addedInfo->args_offset;
105	return B_OK;
106}
107
108
109status_t
110Team::InitThread(Thread* thread)
111{
112	// The thread
113	thread->SetLazyImages(!_SynchronousProfiling());
114
115	// create the sample area
116	char areaName[B_OS_NAME_LENGTH];
117	snprintf(areaName, sizeof(areaName), "profiling samples %" B_PRId32,
118		thread->ID());
119	void* samples;
120	area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
121		SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
122	if (sampleArea < 0) {
123		fprintf(stderr,
124			"%s: Failed to create sample area for thread %" B_PRId32 ": "
125			"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
126		return sampleArea;
127	}
128
129	thread->SetSampleArea(sampleArea, (addr_t*)samples);
130
131	// add the current images to the thread
132	int32 imageCount = fImages.CountItems();
133	for (int32 i = 0; i < imageCount; i++) {
134		status_t error = thread->AddImage(fImages.ItemAt(i));
135		if (error != B_OK)
136			return error;
137	}
138
139	if (!_SynchronousProfiling()) {
140		// set thread debugging flags and start profiling
141		int32 threadDebugFlags = 0;
142//		if (!traceTeam) {
143//			threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
144//				| (traceChildThreads
145//					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
146//		}
147		status_t error = set_thread_debugging_flags(fNubPort, thread->ID(),
148			threadDebugFlags);
149		if (error != B_OK)
150			return error;
151
152		// start profiling
153		debug_nub_start_profiler message;
154		message.reply_port = fDebugContext.reply_port;
155		message.thread = thread->ID();
156		message.interval = gOptions.interval;
157		message.sample_area = sampleArea;
158		message.stack_depth = gOptions.stack_depth;
159		message.variable_stack_depth = gOptions.analyze_full_stack;
160
161		debug_nub_start_profiler_reply reply;
162		error = send_debug_message(&fDebugContext,
163			B_DEBUG_START_PROFILER, &message, sizeof(message), &reply,
164			sizeof(reply));
165		if (error != B_OK || (error = reply.error) != B_OK) {
166			fprintf(stderr,
167				"%s: Failed to start profiler for thread %" B_PRId32 ": %s\n",
168				kCommandName, thread->ID(), strerror(error));
169			return error;
170		}
171
172		thread->SetInterval(reply.interval);
173
174		fThreads.Add(thread);
175
176		// resume the target thread to be sure, it's running
177		resume_thread(thread->ID());
178	} else {
179		// debugger-less profiling
180		thread->SetInterval(gOptions.interval);
181		fThreads.Add(thread);
182	}
183
184	return B_OK;
185}
186
187
188void
189Team::RemoveThread(Thread* thread)
190{
191	fThreads.Remove(thread);
192}
193
194
195void
196Team::Exec(int32 event, const char* args, const char* threadName)
197{
198	// remove all non-kernel images
199	int32 imageCount = fImages.CountItems();
200	for (int32 i = imageCount - 1; i >= 0; i--) {
201		Image* image = fImages.ItemAt(i);
202		if (image->Owner() == ID())
203			_RemoveImage(i, event);
204	}
205
206	fArgs = args;
207
208	// update the main thread
209	ThreadList::Iterator it = fThreads.GetIterator();
210	while (Thread* thread = it.Next()) {
211		if (thread->ID() == ID()) {
212			thread->UpdateInfo(threadName);
213			break;
214		}
215	}
216}
217
218
219status_t
220Team::AddImage(SharedImage* sharedImage, const image_info& imageInfo,
221	team_id owner, int32 event)
222{
223	// create the image
224	Image* image = new(std::nothrow) Image(sharedImage, imageInfo, owner,
225		event);
226	if (image == NULL)
227		return B_NO_MEMORY;
228
229	if (!fImages.AddItem(image)) {
230		delete image;
231		return B_NO_MEMORY;
232	}
233
234	// Although we generally synchronize the threads' images lazily, we have
235	// to add new images at least, since otherwise images could be added
236	// and removed again, and the hits inbetween could never be matched.
237	ThreadList::Iterator it = fThreads.GetIterator();
238	while (Thread* thread = it.Next())
239		thread->AddImage(image);
240
241	return B_OK;
242}
243
244
245status_t
246Team::RemoveImage(image_id imageID, int32 event)
247{
248	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
249		if (image->ID() == imageID) {
250			_RemoveImage(i, event);
251			return B_OK;
252		}
253	}
254
255	return B_ENTRY_NOT_FOUND;
256}
257
258
259Image*
260Team::FindImage(image_id id) const
261{
262	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
263		if (image->ID() == id)
264			return image;
265	}
266
267	return NULL;
268}
269
270
271void
272Team::_RemoveImage(int32 index, int32 event)
273{
274	Image* image = fImages.RemoveItemAt(index);
275	if (image == NULL)
276		return;
277
278	if (_SynchronousProfiling()) {
279		ThreadList::Iterator it = fThreads.GetIterator();
280		while (Thread* thread = it.Next())
281			thread->RemoveImage(image);
282	} else {
283		// Note: We don't tell the threads that the image has been removed. They
284		// will be updated lazily when their next profiler update arrives. This
285		// is necessary, since the update might contain samples hitting that
286		// image.
287	}
288
289	image->SetDeletionEvent(event);
290	image->ReleaseReference();
291}
292