xref: /haiku/src/bin/debug/profile/Team.cpp (revision 323b65468e5836bb27a5e373b14027d902349437)
1 /*
2  * Copyright 2008-2010, 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 #include <system_profiler_defs.h>
14 
15 #include "debug_utils.h"
16 
17 #include "Image.h"
18 #include "Options.h"
19 
20 
21 //#define TRACE_PROFILE_TEAM
22 #ifdef TRACE_PROFILE_TEAM
23 #	define TRACE(x...) printf(x)
24 #else
25 #	define TRACE(x...) do {} while(false)
26 #endif
27 
28 
29 enum {
30 	SAMPLE_AREA_SIZE	= 128 * 1024,
31 };
32 
33 
34 Team::Team()
35 	:
36 	fID(-1),
37 	fNubPort(-1),
38 	fThreads(),
39 	fImages(20, false)
40 {
41 	fDebugContext.nub_port = -1;
42 }
43 
44 
45 Team::~Team()
46 {
47 	if (fDebugContext.nub_port >= 0)
48 		destroy_debug_context(&fDebugContext);
49 
50 	if (fNubPort >= 0)
51 		remove_team_debugger(fID);
52 
53 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++)
54 		image->ReleaseReference();
55 }
56 
57 
58 status_t
59 Team::Init(team_id teamID, port_id debuggerPort)
60 {
61 	// get team info
62 	team_info teamInfo;
63 	status_t error = get_team_info(teamID, &teamInfo);
64 	if (error != B_OK)
65 		return error;
66 
67 	fID = teamID;
68 	fArgs = teamInfo.args;
69 
70 	// install ourselves as the team debugger
71 	fNubPort = install_team_debugger(teamID, debuggerPort);
72 	if (fNubPort < 0) {
73 		fprintf(stderr, "%s: Failed to install as debugger for team %ld: "
74 			"%s\n", kCommandName, teamID, strerror(fNubPort));
75 		return fNubPort;
76 	}
77 
78 	// init debug context
79 	error = init_debug_context(&fDebugContext, teamID, fNubPort);
80 	if (error != B_OK) {
81 		fprintf(stderr, "%s: Failed to init debug context for team %ld: "
82 			"%s\n", kCommandName, teamID, strerror(error));
83 		return error;
84 	}
85 
86 	// set team debugging flags
87 	int32 teamDebugFlags = B_TEAM_DEBUG_THREADS
88 		| B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES;
89 	set_team_debugging_flags(fNubPort, teamDebugFlags);
90 
91 	return B_OK;
92 }
93 
94 
95 status_t
96 Team::Init(system_profiler_team_added* addedInfo)
97 {
98 	fID = addedInfo->team;
99 	fArgs = addedInfo->name + addedInfo->args_offset;
100 	return B_OK;
101 }
102 
103 
104 status_t
105 Team::InitThread(Thread* thread)
106 {
107 	// The thread
108 	thread->SetLazyImages(!_SynchronousProfiling());
109 
110 	// create the sample area
111 	char areaName[B_OS_NAME_LENGTH];
112 	snprintf(areaName, sizeof(areaName), "profiling samples %ld",
113 		thread->ID());
114 	void* samples;
115 	area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
116 		SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
117 	if (sampleArea < 0) {
118 		fprintf(stderr, "%s: Failed to create sample area for thread %ld: "
119 			"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
120 		return sampleArea;
121 	}
122 
123 	thread->SetSampleArea(sampleArea, (addr_t*)samples);
124 
125 	// add the current images to the thread
126 	int32 imageCount = fImages.CountItems();
127 	for (int32 i = 0; i < imageCount; i++) {
128 		status_t error = thread->AddImage(fImages.ItemAt(i));
129 		if (error != B_OK)
130 			return error;
131 	}
132 
133 	if (!_SynchronousProfiling()) {
134 		// set thread debugging flags and start profiling
135 		int32 threadDebugFlags = 0;
136 //		if (!traceTeam) {
137 //			threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
138 //				| (traceChildThreads
139 //					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
140 //		}
141 		set_thread_debugging_flags(fNubPort, thread->ID(), threadDebugFlags);
142 
143 		// start profiling
144 		debug_nub_start_profiler message;
145 		message.reply_port = fDebugContext.reply_port;
146 		message.thread = thread->ID();
147 		message.interval = gOptions.interval;
148 		message.sample_area = sampleArea;
149 		message.stack_depth = gOptions.stack_depth;
150 		message.variable_stack_depth = gOptions.analyze_full_stack;
151 
152 		debug_nub_start_profiler_reply reply;
153 		status_t error = send_debug_message(&fDebugContext,
154 			B_DEBUG_START_PROFILER, &message, sizeof(message), &reply,
155 			sizeof(reply));
156 		if (error != B_OK || (error = reply.error) != B_OK) {
157 			fprintf(stderr, "%s: Failed to start profiler for thread %ld: %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 		resume_thread(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