xref: /haiku/src/bin/debug/profile/Team.cpp (revision 1deede7388b04dbeec5af85cae7164735ea9e70d)
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 	error = set_team_debugging_flags(fNubPort, teamDebugFlags);
93 	if (error != B_OK)
94 		return error;
95 
96 	return B_OK;
97 }
98 
99 
100 status_t
101 Team::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 
109 status_t
110 Team::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 
188 void
189 Team::RemoveThread(Thread* thread)
190 {
191 	fThreads.Remove(thread);
192 }
193 
194 
195 void
196 Team::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 
219 status_t
220 Team::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 
245 status_t
246 Team::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 
259 Image*
260 Team::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 
271 void
272 Team::_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