xref: /haiku/src/bin/debug/profile/Team.cpp (revision 58481f0f6ef1a61ba07283f012cafbc2ed874ead)
1 /*
2  * Copyright 2008-2009, 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 "Options.h"
18 
19 
20 //#define TRACE_PROFILE_TEAM
21 #ifdef TRACE_PROFILE_TEAM
22 #	define TRACE(x...) printf(x)
23 #else
24 #	define TRACE(x...) do {} while(false)
25 #endif
26 
27 
28 enum {
29 	SAMPLE_AREA_SIZE	= 128 * 1024,
30 };
31 
32 
33 Team::Team()
34 	:
35 	fID(-1),
36 	fNubPort(-1),
37 	fThreads(),
38 	fImages(20, false)
39 {
40 	fDebugContext.nub_port = -1;
41 }
42 
43 
44 Team::~Team()
45 {
46 	if (fDebugContext.nub_port >= 0)
47 		destroy_debug_context(&fDebugContext);
48 
49 	if (fNubPort >= 0)
50 		remove_team_debugger(fID);
51 
52 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++)
53 		image->RemoveReference();
54 }
55 
56 
57 status_t
58 Team::Init(team_id teamID, port_id debuggerPort)
59 {
60 	// get team info
61 	team_info teamInfo;
62 	status_t error = get_team_info(teamID, &teamInfo);
63 	if (error != B_OK)
64 		return error;
65 
66 	fID = teamID;
67 	fArgs = teamInfo.args;
68 
69 	// install ourselves as the team debugger
70 	fNubPort = install_team_debugger(teamID, debuggerPort);
71 	if (fNubPort < 0) {
72 		fprintf(stderr, "%s: Failed to install as debugger for team %ld: "
73 			"%s\n", kCommandName, teamID, strerror(fNubPort));
74 		return fNubPort;
75 	}
76 
77 	// init debug context
78 	error = init_debug_context(&fDebugContext, teamID, fNubPort);
79 	if (error != B_OK) {
80 		fprintf(stderr, "%s: Failed to init debug context for team %ld: "
81 			"%s\n", kCommandName, teamID, strerror(error));
82 		return error;
83 	}
84 
85 	// set team debugging flags
86 	int32 teamDebugFlags = B_TEAM_DEBUG_THREADS
87 		| B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES;
88 	set_team_debugging_flags(fNubPort, teamDebugFlags);
89 
90 	return B_OK;
91 }
92 
93 
94 status_t
95 Team::Init(system_profiler_team_added* addedInfo)
96 {
97 	fID = addedInfo->team;
98 	fArgs = addedInfo->name + addedInfo->args_offset;
99 	return B_OK;
100 }
101 
102 
103 status_t
104 Team::InitThread(Thread* thread)
105 {
106 	// The thread
107 	thread->ProfileResult()->SetLazyImages(!_SynchronousProfiling());
108 
109 	// create the sample area
110 	char areaName[B_OS_NAME_LENGTH];
111 	snprintf(areaName, sizeof(areaName), "profiling samples %ld",
112 		thread->ID());
113 	void* samples;
114 	area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
115 		SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
116 	if (sampleArea < 0) {
117 		fprintf(stderr, "%s: Failed to create sample area for thread %ld: "
118 			"%s\n", kCommandName, thread->ID(), strerror(sampleArea));
119 		return sampleArea;
120 	}
121 
122 	thread->SetSampleArea(sampleArea, (addr_t*)samples);
123 
124 	// add the current images to the thread
125 	int32 imageCount = fImages.CountItems();
126 	for (int32 i = 0; i < imageCount; i++) {
127 		status_t error = thread->AddImage(fImages.ItemAt(i));
128 		if (error != B_OK)
129 			return error;
130 	}
131 
132 	if (!_SynchronousProfiling()) {
133 		// set thread debugging flags and start profiling
134 		int32 threadDebugFlags = 0;
135 //		if (!traceTeam) {
136 //			threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL
137 //				| (traceChildThreads
138 //					? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0);
139 //		}
140 		set_thread_debugging_flags(fNubPort, thread->ID(), threadDebugFlags);
141 
142 		// start profiling
143 		debug_nub_start_profiler message;
144 		message.reply_port = fDebugContext.reply_port;
145 		message.thread = thread->ID();
146 		message.interval = gOptions.interval;
147 		message.sample_area = sampleArea;
148 		message.stack_depth = gOptions.stack_depth;
149 		message.variable_stack_depth = gOptions.analyze_full_stack;
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, "%s: Failed to start profiler for thread %ld: %s\n",
157 				kCommandName, thread->ID(), strerror(error));
158 			return error;
159 		}
160 
161 		thread->SetInterval(reply.interval);
162 
163 		fThreads.Add(thread);
164 
165 		// resume the target thread to be sure, it's running
166 		resume_thread(thread->ID());
167 	} else {
168 		// debugger-less profiling
169 		thread->SetInterval(gOptions.interval);
170 		fThreads.Add(thread);
171 	}
172 
173 	return B_OK;
174 }
175 
176 
177 void
178 Team::RemoveThread(Thread* thread)
179 {
180 	fThreads.Remove(thread);
181 }
182 
183 
184 void
185 Team::Exec(int32 event, const char* args, const char* threadName)
186 {
187 	// remove all non-kernel images
188 	int32 imageCount = fImages.CountItems();
189 	for (int32 i = imageCount - 1; i >= 0; i--) {
190 		Image* image = fImages.ItemAt(i);
191 		if (image->Owner() == ID())
192 			_RemoveImage(i, event);
193 	}
194 
195 	fArgs = args;
196 
197 	// update the main thread
198 	ThreadList::Iterator it = fThreads.GetIterator();
199 	while (Thread* thread = it.Next()) {
200 		if (thread->ID() == ID()) {
201 			thread->UpdateInfo(threadName);
202 			break;
203 		}
204 	}
205 }
206 
207 
208 status_t
209 Team::AddImage(SharedImage* sharedImage, const image_info& imageInfo,
210 	team_id owner, int32 event)
211 {
212 	// create the image
213 	Image* image = new(std::nothrow) Image(sharedImage, imageInfo, owner,
214 		event);
215 	if (image == NULL)
216 		return B_NO_MEMORY;
217 
218 	if (!fImages.AddItem(image)) {
219 		delete image;
220 		return B_NO_MEMORY;
221 	}
222 
223 	// Although we generally synchronize the threads' images lazily, we have
224 	// to add new images at least, since otherwise images could be added
225 	// and removed again, and the hits inbetween could never be matched.
226 	ThreadList::Iterator it = fThreads.GetIterator();
227 	while (Thread* thread = it.Next())
228 		thread->AddImage(image);
229 
230 	return B_OK;
231 }
232 
233 
234 status_t
235 Team::RemoveImage(image_id imageID, int32 event)
236 {
237 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
238 		if (image->ID() == imageID) {
239 			_RemoveImage(i, event);
240 			return B_OK;
241 		}
242 	}
243 
244 	return B_ENTRY_NOT_FOUND;
245 }
246 
247 
248 Image*
249 Team::FindImage(image_id id) const
250 {
251 	for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) {
252 		if (image->ID() == id)
253 			return image;
254 	}
255 
256 	return NULL;
257 }
258 
259 
260 void
261 Team::_RemoveImage(int32 index, int32 event)
262 {
263 	Image* image = fImages.RemoveItemAt(index);
264 	if (image == NULL)
265 		return;
266 
267 	if (_SynchronousProfiling()) {
268 		ThreadList::Iterator it = fThreads.GetIterator();
269 		while (Thread* thread = it.Next())
270 			thread->ProfileResult()->RemoveImage(image);
271 	} else {
272 		// Note: We don't tell the threads that the image has been removed. They
273 		// will be updated lazily when their next profiler update arrives. This
274 		// is necessary, since the update might contain samples hitting that
275 		// image.
276 	}
277 
278 	image->SetDeletionEvent(event);
279 	image->RemoveReference();
280 }
281