xref: /haiku/src/bin/debug/profile/Team.cpp (revision b617a7b410c05275effb95f4b2f5608359d9b7b9)
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, "%s: Failed to install as debugger for team %ld: "
75 			"%s\n", kCommandName, teamID, strerror(fNubPort));
76 		return fNubPort;
77 	}
78 
79 	// init debug context
80 	error = init_debug_context(&fDebugContext, teamID, fNubPort);
81 	if (error != B_OK) {
82 		fprintf(stderr, "%s: Failed to init debug context for team %ld: "
83 			"%s\n", kCommandName, teamID, strerror(error));
84 		return error;
85 	}
86 
87 	// set team debugging flags
88 	int32 teamDebugFlags = B_TEAM_DEBUG_THREADS
89 		| B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES;
90 	error = set_team_debugging_flags(fNubPort, teamDebugFlags);
91 	if (error != B_OK)
92 		return error;
93 
94 	return B_OK;
95 }
96 
97 
98 status_t
99 Team::Init(system_profiler_team_added* addedInfo)
100 {
101 	fID = addedInfo->team;
102 	fArgs = addedInfo->name + addedInfo->args_offset;
103 	return B_OK;
104 }
105 
106 
107 status_t
108 Team::InitThread(Thread* thread)
109 {
110 	// The thread
111 	thread->SetLazyImages(!_SynchronousProfiling());
112 
113 	// create the sample area
114 	char areaName[B_OS_NAME_LENGTH];
115 	snprintf(areaName, sizeof(areaName), "profiling samples %ld",
116 		thread->ID());
117 	void* samples;
118 	area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
119 		SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
120 	if (sampleArea < 0) {
121 		fprintf(stderr, "%s: Failed to create sample area for thread %ld: "
122 			"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
123 		return sampleArea;
124 	}
125 
126 	thread->SetSampleArea(sampleArea, (addr_t*)samples);
127 
128 	// add the current images to the thread
129 	int32 imageCount = fImages.CountItems();
130 	for (int32 i = 0; i < imageCount; i++) {
131 		status_t error = thread->AddImage(fImages.ItemAt(i));
132 		if (error != B_OK)
133 			return error;
134 	}
135 
136 	if (!_SynchronousProfiling()) {
137 		// set thread debugging flags and start profiling
138 		int32 threadDebugFlags = 0;
139 //		if (!traceTeam) {
140 //			threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
141 //				| (traceChildThreads
142 //					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
143 //		}
144 		status_t error = set_thread_debugging_flags(fNubPort, thread->ID(),
145 			threadDebugFlags);
146 		if (error != B_OK)
147 			return error;
148 
149 		// start profiling
150 		debug_nub_start_profiler message;
151 		message.reply_port = fDebugContext.reply_port;
152 		message.thread = thread->ID();
153 		message.interval = gOptions.interval;
154 		message.sample_area = sampleArea;
155 		message.stack_depth = gOptions.stack_depth;
156 		message.variable_stack_depth = gOptions.analyze_full_stack;
157 
158 		debug_nub_start_profiler_reply reply;
159 		error = send_debug_message(&fDebugContext,
160 			B_DEBUG_START_PROFILER, &message, sizeof(message), &reply,
161 			sizeof(reply));
162 		if (error != B_OK || (error = reply.error) != B_OK) {
163 			fprintf(stderr, "%s: Failed to start profiler for thread %ld: %s\n",
164 				kCommandName, thread->ID(), strerror(error));
165 			return error;
166 		}
167 
168 		thread->SetInterval(reply.interval);
169 
170 		fThreads.Add(thread);
171 
172 		// resume the target thread to be sure, it's running
173 		resume_thread(thread->ID());
174 	} else {
175 		// debugger-less profiling
176 		thread->SetInterval(gOptions.interval);
177 		fThreads.Add(thread);
178 	}
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, const char* args, const char* threadName)
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 	fArgs = args;
203 
204 	// update the main thread
205 	ThreadList::Iterator it = fThreads.GetIterator();
206 	while (Thread* thread = it.Next()) {
207 		if (thread->ID() == ID()) {
208 			thread->UpdateInfo(threadName);
209 			break;
210 		}
211 	}
212 }
213 
214 
215 status_t
216 Team::AddImage(SharedImage* sharedImage, const image_info& imageInfo,
217 	team_id owner, int32 event)
218 {
219 	// create the image
220 	Image* image = new(std::nothrow) Image(sharedImage, imageInfo, owner,
221 		event);
222 	if (image == NULL)
223 		return B_NO_MEMORY;
224 
225 	if (!fImages.AddItem(image)) {
226 		delete image;
227 		return B_NO_MEMORY;
228 	}
229 
230 	// Although we generally synchronize the threads' images lazily, we have
231 	// to add new images at least, since otherwise images could be added
232 	// and removed again, and the hits inbetween could never be matched.
233 	ThreadList::Iterator it = fThreads.GetIterator();
234 	while (Thread* thread = it.Next())
235 		thread->AddImage(image);
236 
237 	return B_OK;
238 }
239 
240 
241 status_t
242 Team::RemoveImage(image_id imageID, int32 event)
243 {
244 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
245 		if (image->ID() == imageID) {
246 			_RemoveImage(i, event);
247 			return B_OK;
248 		}
249 	}
250 
251 	return B_ENTRY_NOT_FOUND;
252 }
253 
254 
255 Image*
256 Team::FindImage(image_id id) const
257 {
258 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
259 		if (image->ID() == id)
260 			return image;
261 	}
262 
263 	return NULL;
264 }
265 
266 
267 void
268 Team::_RemoveImage(int32 index, int32 event)
269 {
270 	Image* image = fImages.RemoveItemAt(index);
271 	if (image == NULL)
272 		return;
273 
274 	if (_SynchronousProfiling()) {
275 		ThreadList::Iterator it = fThreads.GetIterator();
276 		while (Thread* thread = it.Next())
277 			thread->RemoveImage(image);
278 	} else {
279 		// Note: We don't tell the threads that the image has been removed. They
280 		// will be updated lazily when their next profiler update arrives. This
281 		// is necessary, since the update might contain samples hitting that
282 		// image.
283 	}
284 
285 	image->SetDeletionEvent(event);
286 	image->ReleaseReference();
287 }
288