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 | B_TEAM_DEBUG_STOP_NEW_THREADS; 93 error = set_team_debugging_flags(fNubPort, teamDebugFlags); 94 if (error != B_OK) 95 return error; 96 97 return B_OK; 98 } 99 100 101 status_t 102 Team::Init(system_profiler_team_added* addedInfo) 103 { 104 fID = addedInfo->team; 105 fArgs = addedInfo->name + addedInfo->args_offset; 106 return B_OK; 107 } 108 109 110 status_t 111 Team::InitThread(Thread* thread) 112 { 113 // The thread 114 thread->SetLazyImages(!_SynchronousProfiling()); 115 116 // create the sample area 117 char areaName[B_OS_NAME_LENGTH]; 118 snprintf(areaName, sizeof(areaName), "profiling samples %" B_PRId32, 119 thread->ID()); 120 void* samples; 121 area_id sampleArea = create_area(areaName, &samples, B_ANY_ADDRESS, 122 SAMPLE_AREA_SIZE, B_NO_LOCK, B_READ_AREA | B_WRITE_AREA); 123 if (sampleArea < 0) { 124 fprintf(stderr, 125 "%s: Failed to create sample area for thread %" B_PRId32 ": " 126 "%s\n", kCommandName, thread->ID(), strerror(sampleArea)); 127 return sampleArea; 128 } 129 130 thread->SetSampleArea(sampleArea, (addr_t*)samples); 131 132 // add the current images to the thread 133 int32 imageCount = fImages.CountItems(); 134 for (int32 i = 0; i < imageCount; i++) { 135 status_t error = thread->AddImage(fImages.ItemAt(i)); 136 if (error != B_OK) 137 return error; 138 } 139 140 if (!_SynchronousProfiling()) { 141 // start profiling 142 debug_nub_start_profiler message; 143 message.reply_port = fDebugContext.reply_port; 144 message.thread = thread->ID(); 145 message.interval = gOptions.interval; 146 message.sample_area = sampleArea; 147 message.stack_depth = gOptions.stack_depth; 148 message.variable_stack_depth = gOptions.analyze_full_stack; 149 message.profile_kernel = gOptions.profile_kernel; 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, 157 "%s: Failed to start profiler for thread %" B_PRId32 ": %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 continue_thread(fDebugContext.nub_port, 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