1 /* 2 * Copyright 2008-2010, 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 "Image.h" 18 #include "Options.h" 19 20 21 //#define TRACE_PROFILE_TEAM 22 #ifdef TRACE_PROFILE_TEAM 23 # define TRACE(x...) printf(x) 24 #else 25 # define TRACE(x...) do {} while(false) 26 #endif 27 28 29 enum { 30 SAMPLE_AREA_SIZE = 128 * 1024, 31 }; 32 33 34 Team::Team() 35 : 36 fID(-1), 37 fNubPort(-1), 38 fThreads(), 39 fImages(20, false) 40 { 41 fDebugContext.nub_port = -1; 42 } 43 44 45 Team::~Team() 46 { 47 if (fDebugContext.nub_port >= 0) 48 destroy_debug_context(&fDebugContext); 49 50 if (fNubPort >= 0) 51 remove_team_debugger(fID); 52 53 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) 54 image->ReleaseReference(); 55 } 56 57 58 status_t 59 Team::Init(team_id teamID, port_id debuggerPort) 60 { 61 // get team info 62 team_info teamInfo; 63 status_t error = get_team_info(teamID, &teamInfo); 64 if (error != B_OK) 65 return error; 66 67 fID = teamID; 68 fArgs = teamInfo.args; 69 70 // install ourselves as the team debugger 71 fNubPort = install_team_debugger(teamID, debuggerPort); 72 if (fNubPort < 0) { 73 fprintf(stderr, "%s: Failed to install as debugger for team %ld: " 74 "%s\n", kCommandName, teamID, strerror(fNubPort)); 75 return fNubPort; 76 } 77 78 // init debug context 79 error = init_debug_context(&fDebugContext, teamID, fNubPort); 80 if (error != B_OK) { 81 fprintf(stderr, "%s: Failed to init debug context for team %ld: " 82 "%s\n", kCommandName, teamID, strerror(error)); 83 return error; 84 } 85 86 // set team debugging flags 87 int32 teamDebugFlags = B_TEAM_DEBUG_THREADS 88 | B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES; 89 set_team_debugging_flags(fNubPort, teamDebugFlags); 90 91 return B_OK; 92 } 93 94 95 status_t 96 Team::Init(system_profiler_team_added* addedInfo) 97 { 98 fID = addedInfo->team; 99 fArgs = addedInfo->name + addedInfo->args_offset; 100 return B_OK; 101 } 102 103 104 status_t 105 Team::InitThread(Thread* thread) 106 { 107 // The thread 108 thread->SetLazyImages(!_SynchronousProfiling()); 109 110 // create the sample area 111 char areaName[B_OS_NAME_LENGTH]; 112 snprintf(areaName, sizeof(areaName), "profiling samples %ld", 113 thread->ID()); 114 void* samples; 115 area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS, 116 SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 117 if (sampleArea < 0) { 118 fprintf(stderr, "%s: Failed to create sample area for thread %ld: " 119 "%s\n", kCommandName, thread->ID(), strerror(sampleArea)); 120 return sampleArea; 121 } 122 123 thread->SetSampleArea(sampleArea, (addr_t*)samples); 124 125 // add the current images to the thread 126 int32 imageCount = fImages.CountItems(); 127 for (int32 i = 0; i < imageCount; i++) { 128 status_t error = thread->AddImage(fImages.ItemAt(i)); 129 if (error != B_OK) 130 return error; 131 } 132 133 if (!_SynchronousProfiling()) { 134 // set thread debugging flags and start profiling 135 int32 threadDebugFlags = 0; 136 // if (!traceTeam) { 137 // threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL 138 // | (traceChildThreads 139 // ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0); 140 // } 141 set_thread_debugging_flags(fNubPort, thread->ID(), threadDebugFlags); 142 143 // start profiling 144 debug_nub_start_profiler message; 145 message.reply_port = fDebugContext.reply_port; 146 message.thread = thread->ID(); 147 message.interval = gOptions.interval; 148 message.sample_area = sampleArea; 149 message.stack_depth = gOptions.stack_depth; 150 message.variable_stack_depth = gOptions.analyze_full_stack; 151 152 debug_nub_start_profiler_reply reply; 153 status_t error = send_debug_message(&fDebugContext, 154 B_DEBUG_START_PROFILER, &message, sizeof(message), &reply, 155 sizeof(reply)); 156 if (error != B_OK || (error = reply.error) != B_OK) { 157 fprintf(stderr, "%s: Failed to start profiler for thread %ld: %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 resume_thread(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 179 Team::RemoveThread(Thread* thread) 180 { 181 fThreads.Remove(thread); 182 } 183 184 185 void 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 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 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* 250 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 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