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