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