1 /* 2 * Copyright 2008-2009, 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 "Options.h" 18 19 20 //#define TRACE_PROFILE_TEAM 21 #ifdef TRACE_PROFILE_TEAM 22 # define TRACE(x...) printf(x) 23 #else 24 # define TRACE(x...) do {} while(false) 25 #endif 26 27 28 enum { 29 SAMPLE_AREA_SIZE = 128 * 1024, 30 }; 31 32 33 Team::Team() 34 : 35 fID(-1), 36 fNubPort(-1), 37 fThreads(), 38 fImages(20, false) 39 { 40 fDebugContext.nub_port = -1; 41 } 42 43 44 Team::~Team() 45 { 46 if (fDebugContext.nub_port >= 0) 47 destroy_debug_context(&fDebugContext); 48 49 if (fNubPort >= 0) 50 remove_team_debugger(fID); 51 52 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) 53 image->RemoveReference(); 54 } 55 56 57 status_t 58 Team::Init(team_id teamID, port_id debuggerPort) 59 { 60 // get team info 61 team_info teamInfo; 62 status_t error = get_team_info(teamID, &teamInfo); 63 if (error != B_OK) 64 return error; 65 66 fID = teamID; 67 fArgs = teamInfo.args; 68 69 // install ourselves as the team debugger 70 fNubPort = install_team_debugger(teamID, debuggerPort); 71 if (fNubPort < 0) { 72 fprintf(stderr, "%s: Failed to install as debugger for team %ld: " 73 "%s\n", kCommandName, teamID, strerror(fNubPort)); 74 return fNubPort; 75 } 76 77 // init debug context 78 error = init_debug_context(&fDebugContext, teamID, fNubPort); 79 if (error != B_OK) { 80 fprintf(stderr, "%s: Failed to init debug context for team %ld: " 81 "%s\n", kCommandName, teamID, strerror(error)); 82 return error; 83 } 84 85 // set team debugging flags 86 int32 teamDebugFlags = B_TEAM_DEBUG_THREADS 87 | B_TEAM_DEBUG_TEAM_CREATION | B_TEAM_DEBUG_IMAGES; 88 set_team_debugging_flags(fNubPort, teamDebugFlags); 89 90 return B_OK; 91 } 92 93 94 status_t 95 Team::Init(system_profiler_team_added* addedInfo) 96 { 97 fID = addedInfo->team; 98 fArgs = addedInfo->name + addedInfo->args_offset; 99 return B_OK; 100 } 101 102 103 status_t 104 Team::InitThread(Thread* thread) 105 { 106 // The thread 107 thread->GetProfileResult()->SetLazyImages(!_SynchronousProfiling()); 108 109 // create the sample area 110 char areaName[B_OS_NAME_LENGTH]; 111 snprintf(areaName, sizeof(areaName), "profiling samples %ld", 112 thread->ID()); 113 void* samples; 114 area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS, 115 SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 116 if (sampleArea < 0) { 117 fprintf(stderr, "%s: Failed to create sample area for thread %ld: " 118 "%s\n", kCommandName, thread->ID(), strerror(sampleArea)); 119 return sampleArea; 120 } 121 122 thread->SetSampleArea(sampleArea, (addr_t*)samples); 123 124 // add the current images to the thread 125 int32 imageCount = fImages.CountItems(); 126 for (int32 i = 0; i < imageCount; i++) { 127 status_t error = thread->AddImage(fImages.ItemAt(i)); 128 if (error != B_OK) 129 return error; 130 } 131 132 if (!_SynchronousProfiling()) { 133 // set thread debugging flags and start profiling 134 int32 threadDebugFlags = 0; 135 // if (!traceTeam) { 136 // threadDebugFlags = B_THREAD_DEBUG_POST_SYSCALL 137 // | (traceChildThreads 138 // ? B_THREAD_DEBUG_SYSCALL_TRACE_CHILD_THREADS : 0); 139 // } 140 set_thread_debugging_flags(fNubPort, thread->ID(), threadDebugFlags); 141 142 // start profiling 143 debug_nub_start_profiler message; 144 message.reply_port = fDebugContext.reply_port; 145 message.thread = thread->ID(); 146 message.interval = gOptions.interval; 147 message.sample_area = sampleArea; 148 message.stack_depth = gOptions.stack_depth; 149 message.variable_stack_depth = gOptions.analyze_full_stack; 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, "%s: Failed to start profiler for thread %ld: %s\n", 157 kCommandName, thread->ID(), strerror(error)); 158 return error; 159 } 160 161 thread->SetInterval(reply.interval); 162 163 fThreads.Add(thread); 164 165 // resume the target thread to be sure, it's running 166 resume_thread(thread->ID()); 167 } else { 168 // debugger-less profiling 169 thread->SetInterval(gOptions.interval); 170 fThreads.Add(thread); 171 } 172 173 return B_OK; 174 } 175 176 177 void 178 Team::RemoveThread(Thread* thread) 179 { 180 fThreads.Remove(thread); 181 } 182 183 184 void 185 Team::Exec(int32 event, const char* args, const char* threadName) 186 { 187 // remove all non-kernel images 188 int32 imageCount = fImages.CountItems(); 189 for (int32 i = imageCount - 1; i >= 0; i--) { 190 Image* image = fImages.ItemAt(i); 191 if (image->Owner() == ID()) 192 _RemoveImage(i, event); 193 } 194 195 fArgs = args; 196 197 // update the main thread 198 ThreadList::Iterator it = fThreads.GetIterator(); 199 while (Thread* thread = it.Next()) { 200 if (thread->ID() == ID()) { 201 thread->UpdateInfo(threadName); 202 break; 203 } 204 } 205 } 206 207 208 status_t 209 Team::AddImage(SharedImage* sharedImage, const image_info& imageInfo, 210 team_id owner, int32 event) 211 { 212 // create the image 213 Image* image = new(std::nothrow) Image(sharedImage, imageInfo, owner, 214 event); 215 if (image == NULL) 216 return B_NO_MEMORY; 217 218 if (!fImages.AddItem(image)) { 219 delete image; 220 return B_NO_MEMORY; 221 } 222 223 // Although we generally synchronize the threads' images lazily, we have 224 // to add new images at least, since otherwise images could be added 225 // and removed again, and the hits inbetween could never be matched. 226 ThreadList::Iterator it = fThreads.GetIterator(); 227 while (Thread* thread = it.Next()) 228 thread->AddImage(image); 229 230 return B_OK; 231 } 232 233 234 status_t 235 Team::RemoveImage(image_id imageID, int32 event) 236 { 237 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) { 238 if (image->ID() == imageID) { 239 _RemoveImage(i, event); 240 return B_OK; 241 } 242 } 243 244 return B_ENTRY_NOT_FOUND; 245 } 246 247 248 Image* 249 Team::FindImage(image_id id) const 250 { 251 for (int32 i = 0; Image* image = fImages.ItemAt(i); i++) { 252 if (image->ID() == id) 253 return image; 254 } 255 256 return NULL; 257 } 258 259 260 void 261 Team::_RemoveImage(int32 index, int32 event) 262 { 263 Image* image = fImages.RemoveItemAt(index); 264 if (image == NULL) 265 return; 266 267 if (_SynchronousProfiling()) { 268 ThreadList::Iterator it = fThreads.GetIterator(); 269 while (Thread* thread = it.Next()) 270 thread->GetProfileResult()->RemoveImage(image); 271 } else { 272 // Note: We don't tell the threads that the image has been removed. They 273 // will be updated lazily when their next profiler update arrives. This 274 // is necessary, since the update might contain samples hitting that 275 // image. 276 } 277 278 image->SetDeletionEvent(event); 279 image->RemoveReference(); 280 } 281