1 /* 2 * Copyright 2008, 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 14 #include "debug_utils.h" 15 16 #include "Options.h" 17 18 19 enum { 20 SAMPLE_AREA_SIZE = 128 * 1024, 21 }; 22 23 24 Team::Team() 25 : 26 fNubPort(-1), 27 fThreads(), 28 fImages(20, false) 29 { 30 fInfo.team = -1; 31 fDebugContext.nub_port = -1; 32 } 33 34 35 Team::~Team() 36 { 37 if (fDebugContext.nub_port >= 0) 38 destroy_debug_context(&fDebugContext); 39 40 if (fNubPort >= 0) 41 remove_team_debugger(fInfo.team); 42 43 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) 44 image->RemoveReference(); 45 } 46 47 48 status_t 49 Team::Init(team_id teamID, port_id debuggerPort) 50 { 51 // get team info 52 status_t error = get_team_info(teamID, &fInfo); 53 if (error != B_OK) 54 return error; 55 56 // install ourselves as the team debugger 57 fNubPort = install_team_debugger(teamID, debuggerPort); 58 if (fNubPort < 0) { 59 fprintf(stderr, "%s: Failed to install as debugger for team %ld: " 60 "%s\n", kCommandName, teamID, strerror(fNubPort)); 61 return fNubPort; 62 } 63 64 // init debug context 65 error = init_debug_context(&fDebugContext, teamID, fNubPort); 66 if (error != B_OK) { 67 fprintf(stderr, "%s: Failed to init debug context for team %ld: " 68 "%s\n", kCommandName, teamID, strerror(error)); 69 return error; 70 } 71 72 // create symbol lookup context 73 debug_symbol_lookup_context* lookupContext; 74 error = debug_create_symbol_lookup_context(&fDebugContext, 75 &lookupContext); 76 if (error != B_OK) { 77 fprintf(stderr, "%s: Failed to create symbol lookup context for " 78 "team %ld: %s\n", kCommandName, teamID, strerror(error)); 79 return error; 80 } 81 82 // load the team's images and their symbols 83 error = _LoadSymbols(lookupContext, ID()); 84 debug_delete_symbol_lookup_context(lookupContext); 85 if (error != B_OK) 86 return error; 87 88 // also try to load the kernel images and symbols 89 if (gOptions.profile_kernel) { 90 // fake a debug context -- it's not really needed anyway 91 debug_context debugContext; 92 debugContext.team = B_SYSTEM_TEAM; 93 debugContext.nub_port = -1; 94 debugContext.reply_port = -1; 95 96 // create symbol lookup context 97 error = debug_create_symbol_lookup_context(&debugContext, 98 &lookupContext); 99 if (error != B_OK) { 100 fprintf(stderr, "%s: Failed to create symbol lookup context " 101 "for the kernel team: %s\n", kCommandName, strerror(error)); 102 return error; 103 } 104 105 // load the kernel's images and their symbols 106 _LoadSymbols(lookupContext, B_SYSTEM_TEAM); 107 debug_delete_symbol_lookup_context(lookupContext); 108 } 109 110 // set team debugging flags 111 int32 teamDebugFlags = B_TEAM_DEBUG_THREADS 112 | B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES; 113 set_team_debugging_flags(fNubPort, teamDebugFlags); 114 115 return B_OK; 116 } 117 118 119 status_t 120 Team::InitThread(Thread* thread) 121 { 122 // create the sample area 123 char areaName[B_OS_NAME_LENGTH]; 124 snprintf(areaName, sizeof(areaName), "profiling samples %ld", 125 thread->ID()); 126 void* samples; 127 area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS, 128 SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 129 if (sampleArea < 0) { 130 fprintf(stderr, "%s: Failed to create sample area for thread %ld: " 131 "%s\n", kCommandName, thread->ID(), strerror(sampleArea)); 132 return sampleArea; 133 } 134 135 thread->SetSampleArea(sampleArea, (addr_t*)samples); 136 137 // add the current images to the thread 138 int32 imageCount = fImages.CountItems(); 139 for (int32 i = 0; i < imageCount; i++) { 140 status_t error = thread->AddImage(fImages.ItemAt(i)); 141 if (error != B_OK) 142 return error; 143 } 144 145 // set thread debugging flags and start profiling 146 int32 threadDebugFlags = 0; 147 // if (!traceTeam) { 148 // threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL 149 // | (traceChildThreads 150 // ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0); 151 // } 152 set_thread_debugging_flags(fNubPort, thread->ID(), threadDebugFlags); 153 154 // start profiling 155 debug_nub_start_profiler message; 156 message.reply_port = fDebugContext.reply_port; 157 message.thread = thread->ID(); 158 message.interval = gOptions.interval; 159 message.sample_area = sampleArea; 160 message.stack_depth = gOptions.stack_depth; 161 message.variable_stack_depth = gOptions.analyze_full_stack; 162 163 debug_nub_start_profiler_reply reply; 164 status_t error = send_debug_message(&fDebugContext, 165 B_DEBUG_START_PROFILER, &message, sizeof(message), &reply, 166 sizeof(reply)); 167 if (error != B_OK || (error = reply.error) != B_OK) { 168 fprintf(stderr, "%s: Failed to start profiler for thread %ld: %s\n", 169 kCommandName, thread->ID(), strerror(error)); 170 return error; 171 } 172 173 thread->SetInterval(reply.interval); 174 175 fThreads.Add(thread); 176 177 // resume the target thread to be sure, it's running 178 resume_thread(thread->ID()); 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) 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 // update the main thread 203 ThreadList::Iterator it = fThreads.GetIterator(); 204 while (Thread* thread = it.Next()) { 205 if (thread->ID() == ID()) { 206 thread->UpdateInfo(); 207 break; 208 } 209 } 210 } 211 212 213 status_t 214 Team::AddImage(const image_info& imageInfo, int32 event) 215 { 216 // create symbol lookup context 217 debug_symbol_lookup_context* lookupContext; 218 status_t error = debug_create_symbol_lookup_context(&fDebugContext, 219 &lookupContext); 220 if (error != B_OK) { 221 fprintf(stderr, "%s: Failed to create symbol lookup context for " 222 "team %ld: %s\n", kCommandName, ID(), strerror(error)); 223 return error; 224 } 225 226 Image* image; 227 error = _LoadImageSymbols(lookupContext, imageInfo, ID(), event, 228 &image); 229 debug_delete_symbol_lookup_context(lookupContext); 230 231 // Although we generally synchronize the threads' images lazily, we have 232 // to add new images at least, since otherwise images could be added 233 // and removed again, and the hits inbetween could never be matched. 234 if (error == B_OK) { 235 ThreadList::Iterator it = fThreads.GetIterator(); 236 while (Thread* thread = it.Next()) 237 thread->AddImage(image); 238 } 239 240 return error; 241 } 242 243 244 status_t 245 Team::RemoveImage(const image_info& imageInfo, int32 event) 246 { 247 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) { 248 if (image->ID() == imageInfo.id) { 249 _RemoveImage(i, event); 250 return B_OK; 251 } 252 } 253 254 return B_ENTRY_NOT_FOUND; 255 } 256 257 258 Image* 259 Team::FindImage(image_id id) const 260 { 261 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) { 262 if (image->ID() == id) 263 return image; 264 } 265 266 return NULL; 267 } 268 269 270 status_t 271 Team::_LoadSymbols(debug_symbol_lookup_context* lookupContext, 272 team_id owner) 273 { 274 // iterate through the team's images and collect the symbols 275 image_info imageInfo; 276 int32 cookie = 0; 277 while (get_next_image_info(owner, &cookie, &imageInfo) == B_OK) { 278 status_t error = _LoadImageSymbols(lookupContext, imageInfo, 279 owner, 0); 280 if (error == B_NO_MEMORY) 281 return error; 282 } 283 284 return B_OK; 285 } 286 287 288 status_t 289 Team::_LoadImageSymbols(debug_symbol_lookup_context* lookupContext, 290 const image_info& imageInfo, team_id owner, int32 event, Image** _image) 291 { 292 Image* image = new(std::nothrow) Image(imageInfo, owner, event); 293 if (image == NULL) 294 return B_NO_MEMORY; 295 296 status_t error = image->LoadSymbols(lookupContext); 297 if (error != B_OK) { 298 delete image; 299 return error; 300 } 301 302 if (!fImages.AddItem(image)) { 303 delete image; 304 return B_NO_MEMORY; 305 } 306 307 if (_image != NULL) 308 *_image = image; 309 310 return B_OK; 311 } 312 313 314 void 315 Team::_RemoveImage(int32 index, int32 event) 316 { 317 Image* image = fImages.RemoveItemAt(index); 318 if (image == NULL) 319 return; 320 321 // Note: We don't tell the threads that the image has been removed. They 322 // will be updated lazily when their next profiler update arrives. This 323 // is necessary, since the update might contain samples hitting that 324 // image. 325 326 image->SetDeletionEvent(event); 327 image->RemoveReference(); 328 } 329