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 30enum { 31 SAMPLE_AREA_SIZE = 128 * 1024, 32}; 33 34 35Team::Team() 36 : 37 fID(-1), 38 fNubPort(-1), 39 fThreads(), 40 fImages(20, false) 41{ 42 fDebugContext.nub_port = -1; 43} 44 45 46Team::~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 59status_t 60Team::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 100status_t 101Team::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 109status_t 110Team::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 188void 189Team::RemoveThread(Thread* thread) 190{ 191 fThreads.Remove(thread); 192} 193 194 195void 196Team::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 219status_t 220Team::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 245status_t 246Team::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 259Image* 260Team::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 271void 272Team::_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