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
Team()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
~Team()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
Init(team_id teamID,port_id debuggerPort)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 | B_TEAM_DEBUG_STOP_NEW_THREADS;
93 error = set_team_debugging_flags(fNubPort, teamDebugFlags);
94 if (error != B_OK)
95 return error;
96
97 return B_OK;
98 }
99
100
101 status_t
Init(system_profiler_team_added * addedInfo)102 Team::Init(system_profiler_team_added* addedInfo)
103 {
104 fID = addedInfo->team;
105 fArgs = addedInfo->name + addedInfo->args_offset;
106 return B_OK;
107 }
108
109
110 status_t
InitThread(Thread * thread)111 Team::InitThread(Thread* thread)
112 {
113 // The thread
114 thread->SetLazyImages(!_SynchronousProfiling());
115
116 // create the sample area
117 char areaName[B_OS_NAME_LENGTH];
118 snprintf(areaName, sizeof(areaName), "profiling samples %" B_PRId32,
119 thread->ID());
120 void* samples;
121 area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS,
122 SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA);
123 if (sampleArea < 0) {
124 fprintf(stderr,
125 "%s: Failed to create sample area for thread %" B_PRId32 ": "
126 "%s\n", kCommandName, thread->ID(), strerror(sampleArea));
127 return sampleArea;
128 }
129
130 thread->SetSampleArea(sampleArea, (addr_t*)samples);
131
132 // add the current images to the thread
133 int32 imageCount = fImages.CountItems();
134 for (int32 i = 0; i < imageCount; i++) {
135 status_t error = thread->AddImage(fImages.ItemAt(i));
136 if (error != B_OK)
137 return error;
138 }
139
140 if (!_SynchronousProfiling()) {
141 // start profiling
142 debug_nub_start_profiler message;
143 message.reply_port = fDebugContext.reply_port;
144 message.thread = thread->ID();
145 message.interval = gOptions.interval;
146 message.sample_area = sampleArea;
147 message.stack_depth = gOptions.stack_depth;
148 message.variable_stack_depth = gOptions.analyze_full_stack;
149 message.profile_kernel = gOptions.profile_kernel;
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,
157 "%s: Failed to start profiler for thread %" B_PRId32 ": %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 continue_thread(fDebugContext.nub_port, 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
RemoveThread(Thread * thread)179 Team::RemoveThread(Thread* thread)
180 {
181 fThreads.Remove(thread);
182 }
183
184
185 void
Exec(int32 event,const char * args,const char * threadName)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
AddImage(SharedImage * sharedImage,const image_info & imageInfo,team_id owner,int32 event)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
RemoveImage(image_id imageID,int32 event)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*
FindImage(image_id id) const250 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
_RemoveImage(int32 index,int32 event)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