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