xref: /haiku/src/bin/debug/profile/Team.cpp (revision 9a6a20d4689307142a7ed26a1437ba47e244e73f)
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 
30 enum {
31 	SAMPLE_AREA_SIZE	= 128 * 1024,
32 };
33 
34 
35 Team::Team()
36 	:
37 	fID(-1),
38 	fNubPort(-1),
39 	fThreads(),
40 	fImages(20, false)
41 {
42 	fDebugContext.nub_port = -1;
43 }
44 
45 
46 Team::~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 
59 status_t
60 Team::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 		| B_TEAM_DEBUG_STOP_NEW_THREADS;
93 	error = set_team_debugging_flags(fNubPort, teamDebugFlags);
94 	if (error != B_OK)
95 		return error;
96 
97 	return B_OK;
98 }
99 
100 
101 status_t
102 Team::Init(system_profiler_team_added* addedInfo)
103 {
104 	fID = addedInfo->team;
105 	fArgs = addedInfo->name + addedInfo->args_offset;
106 	return B_OK;
107 }
108 
109 
110 status_t
111 Team::InitThread(Thread* thread)
112 {
113 	// The thread
114 	thread->SetLazyImages(!_SynchronousProfiling());
115 
116 	// create the sample area
117 	char areaName[B_OS_NAME_LENGTH];
118 	snprintf(areaName, sizeof(areaName), "profiling samples %" B_PRId32,
119 		thread->ID());
120 	void* samples;
121 	area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
122 		SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
123 	if (sampleArea < 0) {
124 		fprintf(stderr,
125 			"%s: Failed to create sample area for thread %" B_PRId32 ": "
126 			"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
127 		return sampleArea;
128 	}
129 
130 	thread->SetSampleArea(sampleArea, (addr_t*)samples);
131 
132 	// add the current images to the thread
133 	int32 imageCount = fImages.CountItems();
134 	for (int32 i = 0; i < imageCount; i++) {
135 		status_t error = thread->AddImage(fImages.ItemAt(i));
136 		if (error != B_OK)
137 			return error;
138 	}
139 
140 	if (!_SynchronousProfiling()) {
141 		// start profiling
142 		debug_nub_start_profiler message;
143 		message.reply_port = fDebugContext.reply_port;
144 		message.thread = thread->ID();
145 		message.interval = gOptions.interval;
146 		message.sample_area = sampleArea;
147 		message.stack_depth = gOptions.stack_depth;
148 		message.variable_stack_depth = gOptions.analyze_full_stack;
149 		message.profile_kernel = gOptions.profile_kernel;
150 
151 		debug_nub_start_profiler_reply reply;
152 		status_t error = send_debug_message(&fDebugContext,
153 			B_DEBUG_START_PROFILER, &message, sizeof(message), &reply,
154 			sizeof(reply));
155 		if (error != B_OK || (error = reply.error) != B_OK) {
156 			fprintf(stderr,
157 				"%s: Failed to start profiler for thread %" B_PRId32 ": %s\n",
158 				kCommandName, thread->ID(), strerror(error));
159 			return error;
160 		}
161 
162 		thread->SetInterval(reply.interval);
163 
164 		fThreads.Add(thread);
165 
166 		// resume the target thread to be sure it's running
167 		continue_thread(fDebugContext.nub_port, thread->ID());
168 	} else {
169 		// debugger-less profiling
170 		thread->SetInterval(gOptions.interval);
171 		fThreads.Add(thread);
172 	}
173 
174 	return B_OK;
175 }
176 
177 
178 void
179 Team::RemoveThread(Thread* thread)
180 {
181 	fThreads.Remove(thread);
182 }
183 
184 
185 void
186 Team::Exec(int32 event, const char* args, const char* threadName)
187 {
188 	// remove all non-kernel images
189 	int32 imageCount = fImages.CountItems();
190 	for (int32 i = imageCount - 1; i >= 0; i--) {
191 		Image* image = fImages.ItemAt(i);
192 		if (image->Owner() == ID())
193 			_RemoveImage(i, event);
194 	}
195 
196 	fArgs = args;
197 
198 	// update the main thread
199 	ThreadList::Iterator it = fThreads.GetIterator();
200 	while (Thread* thread = it.Next()) {
201 		if (thread->ID() == ID()) {
202 			thread->UpdateInfo(threadName);
203 			break;
204 		}
205 	}
206 }
207 
208 
209 status_t
210 Team::AddImage(SharedImage* sharedImage, const image_info& imageInfo,
211 	team_id owner, int32 event)
212 {
213 	// create the image
214 	Image* image = new(std::nothrow) Image(sharedImage, imageInfo, owner,
215 		event);
216 	if (image == NULL)
217 		return B_NO_MEMORY;
218 
219 	if (!fImages.AddItem(image)) {
220 		delete image;
221 		return B_NO_MEMORY;
222 	}
223 
224 	// Although we generally synchronize the threads' images lazily, we have
225 	// to add new images at least, since otherwise images could be added
226 	// and removed again, and the hits inbetween could never be matched.
227 	ThreadList::Iterator it = fThreads.GetIterator();
228 	while (Thread* thread = it.Next())
229 		thread->AddImage(image);
230 
231 	return B_OK;
232 }
233 
234 
235 status_t
236 Team::RemoveImage(image_id imageID, int32 event)
237 {
238 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
239 		if (image->ID() == imageID) {
240 			_RemoveImage(i, event);
241 			return B_OK;
242 		}
243 	}
244 
245 	return B_ENTRY_NOT_FOUND;
246 }
247 
248 
249 Image*
250 Team::FindImage(image_id id) const
251 {
252 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
253 		if (image->ID() == id)
254 			return image;
255 	}
256 
257 	return NULL;
258 }
259 
260 
261 void
262 Team::_RemoveImage(int32 index, int32 event)
263 {
264 	Image* image = fImages.RemoveItemAt(index);
265 	if (image == NULL)
266 		return;
267 
268 	if (_SynchronousProfiling()) {
269 		ThreadList::Iterator it = fThreads.GetIterator();
270 		while (Thread* thread = it.Next())
271 			thread->RemoveImage(image);
272 	} else {
273 		// Note: We don't tell the threads that the image has been removed. They
274 		// will be updated lazily when their next profiler update arrives. This
275 		// is necessary, since the update might contain samples hitting that
276 		// image.
277 	}
278 
279 	image->SetDeletionEvent(event);
280 	image->ReleaseReference();
281 }
282