1 /* 2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "CallgrindProfileResult.h" 8 9 #include <errno.h> 10 #include <sys/stat.h> 11 12 #include <algorithm> 13 #include <new> 14 #include <StackOrHeapArray.h> 15 16 #include "Options.h" 17 #include "ProfiledEntity.h" 18 19 20 // #pragma mark - CallgrindImageProfileResult 21 22 23 CallgrindImageProfileResult::CallgrindImageProfileResult(SharedImage* image, 24 image_id id) 25 : 26 ImageProfileResult(image, id), 27 fFunctions(NULL), 28 fOutputIndex(0) 29 { 30 } 31 32 33 CallgrindImageProfileResult::~CallgrindImageProfileResult() 34 { 35 int32 symbolCount = fImage->SymbolCount(); 36 for (int32 i = 0; i < symbolCount; i++) { 37 while (CallgrindCalledFunction* calledFunction 38 = fFunctions[i].calledFunctions) { 39 fFunctions[i].calledFunctions = calledFunction->next; 40 delete calledFunction; 41 } 42 } 43 44 delete[] fFunctions; 45 } 46 47 48 status_t 49 CallgrindImageProfileResult::Init() 50 { 51 int32 symbolCount = fImage->SymbolCount(); 52 fFunctions = new(std::nothrow) CallgrindFunction[symbolCount]; 53 if (fFunctions == NULL) 54 return B_NO_MEMORY; 55 56 memset(fFunctions, 0, sizeof(CallgrindFunction) * symbolCount); 57 58 return B_OK; 59 } 60 61 62 void 63 CallgrindImageProfileResult::AddSymbolHit(int32 symbolIndex, 64 CallgrindImageProfileResult* calledImage, int32 calledSymbol) 65 66 { 67 fTotalHits++; 68 69 CallgrindFunction& function = fFunctions[symbolIndex]; 70 if (calledImage != NULL) { 71 // check whether the called function is known already 72 CallgrindCalledFunction* calledFunction = function.calledFunctions; 73 while (calledFunction != NULL) { 74 if (calledFunction->image == calledImage 75 && calledFunction->function == calledSymbol) { 76 break; 77 } 78 calledFunction = calledFunction->next; 79 } 80 81 // create a new CallgrindCalledFunction object, if not known 82 if (calledFunction == NULL) { 83 calledFunction = new(std::nothrow) CallgrindCalledFunction( 84 calledImage, calledSymbol); 85 if (calledFunction == NULL) 86 return; 87 88 calledFunction->next = function.calledFunctions; 89 function.calledFunctions = calledFunction; 90 } 91 92 calledFunction->hits++; 93 } else 94 function.hits++; 95 } 96 97 98 CallgrindFunction* 99 CallgrindImageProfileResult::Functions() const 100 { 101 return fFunctions; 102 } 103 104 105 int32 106 CallgrindImageProfileResult::OutputIndex() const 107 { 108 return fOutputIndex; 109 } 110 111 112 void 113 CallgrindImageProfileResult::SetOutputIndex(int32 index) 114 { 115 fOutputIndex = index; 116 } 117 118 119 // #pragma mark - CallgrindProfileResult 120 121 122 CallgrindProfileResult::CallgrindProfileResult() 123 : 124 fTotalTicks(0), 125 fUnkownTicks(0), 126 fExpectedTicks(0), 127 fDroppedTicks(0), 128 fNextImageOutputIndex(1), 129 fNextFunctionOutputIndex(1) 130 { 131 } 132 133 134 void 135 CallgrindProfileResult::AddSamples(ImageProfileResultContainer* container, 136 addr_t* samples, int32 sampleCount) 137 { 138 int32 unknownSamples = 0; 139 CallgrindImageProfileResult* previousImage = NULL; 140 int32 previousSymbol = -1; 141 142 // TODO: That probably doesn't work with recursive functions. 143 for (int32 i = 0; i < sampleCount; i++) { 144 addr_t address = samples[i]; 145 addr_t loadDelta; 146 CallgrindImageProfileResult* image 147 = static_cast<CallgrindImageProfileResult*>( 148 container->FindImage(address, loadDelta)); 149 int32 symbol = -1; 150 if (image != NULL) { 151 symbol = image->GetImage()->FindSymbol(address - loadDelta); 152 if (symbol >= 0) { 153 image->AddSymbolHit(symbol, previousImage, previousSymbol); 154 previousImage = image; 155 previousSymbol = symbol; 156 } 157 } else 158 unknownSamples++; 159 } 160 161 if (unknownSamples == sampleCount) 162 fUnkownTicks++; 163 164 fTotalTicks++; 165 } 166 167 168 void 169 CallgrindProfileResult::AddExpectedTicks(int32 expected) 170 { 171 fExpectedTicks += expected; 172 } 173 174 175 void 176 CallgrindProfileResult::AddDroppedTicks(int32 dropped) 177 { 178 fDroppedTicks += dropped; 179 } 180 181 182 void 183 CallgrindProfileResult::PrintResults(ImageProfileResultContainer* container) 184 { 185 // create output file 186 187 // create output dir 188 mkdir(gOptions.callgrind_directory, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); 189 190 // get the entity name and replace slashes by hyphens 191 char entityName[B_OS_NAME_LENGTH]; 192 strlcpy(entityName, fEntity->EntityName(), sizeof(entityName)); 193 char* slash = entityName; 194 while ((slash = strchr(slash, '/')) != NULL) 195 *slash = '-'; 196 197 // create the file name 198 char fileName[B_PATH_NAME_LENGTH]; 199 snprintf(fileName, sizeof(fileName), 200 "%s/callgrind.out.%" B_PRId32 ".%s.%" B_PRId64 "ms", 201 gOptions.callgrind_directory, fEntity->EntityID(), entityName, 202 fTotalTicks * fInterval); 203 204 // create the file 205 FILE* out = fopen(fileName, "w+"); 206 if (out == NULL) { 207 fprintf(stderr, "%s: Failed to open output file \"%s\": %s\n", 208 kCommandName, fileName, strerror(errno)); 209 return; 210 } 211 212 // write the header 213 fprintf(out, "version: 1\n"); 214 fprintf(out, "creator: Haiku profile\n"); 215 fprintf(out, "pid: %" B_PRId32 "\n", fEntity->EntityID()); 216 fprintf(out, "cmd: %s\n", fEntity->EntityName()); 217 fprintf(out, "part: 1\n\n"); 218 219 fprintf(out, "positions: line\n"); 220 fprintf(out, "events: Ticks Time\n"); 221 fprintf(out, "summary: %" B_PRId64 " %" B_PRId64 "\n", 222 fTotalTicks, fTotalTicks * fInterval); 223 224 // get hit images 225 BStackOrHeapArray<CallgrindImageProfileResult*, 128> images(container->CountImages()); 226 int32 imageCount = GetHitImages(container, &*images); 227 228 for (int32 i = 0; i < imageCount; i++) { 229 CallgrindImageProfileResult* image = images[i]; 230 231 CallgrindFunction* functions = image->Functions(); 232 int32 imageSymbolCount = image->GetImage()->SymbolCount(); 233 for (int32 k = 0; k < imageSymbolCount; k++) { 234 CallgrindFunction& function = functions[k]; 235 if (function.hits == 0 && function.calledFunctions == NULL) 236 continue; 237 238 fprintf(out, "\n"); 239 _PrintFunction(out, image, k, false); 240 fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\n", function.hits, 241 function.hits * fInterval); 242 243 CallgrindCalledFunction* calledFunction = function.calledFunctions; 244 while (calledFunction != NULL) { 245 _PrintFunction(out, calledFunction->image, 246 calledFunction->function, true); 247 fprintf(out, "calls=%" B_PRId64 " 0\n", calledFunction->hits); 248 fprintf(out, "0 %" B_PRId64 " %" B_PRId64 "\n", 249 calledFunction->hits, calledFunction->hits * fInterval); 250 calledFunction = calledFunction->next; 251 } 252 } 253 } 254 255 // print pseudo-functions for unknown and dropped ticks 256 if (fUnkownTicks + fDroppedTicks > 0) { 257 fprintf(out, "\nob=<pseudo>\n"); 258 259 if (fUnkownTicks > 0) { 260 fprintf(out, "\nfn=unknown\n"); 261 fprintf(out, "0 %" B_PRId64 "\n", fUnkownTicks); 262 } 263 264 if (fDroppedTicks > 0) { 265 fprintf(out, "\nfn=dropped\n"); 266 fprintf(out, "0 %" B_PRId64 "\n", fDroppedTicks); 267 } 268 } 269 270 fprintf(out, "\ntotals: %" B_PRId64 " %" B_PRId64 "\n", 271 fTotalTicks, fTotalTicks * fInterval); 272 273 fclose(out); 274 } 275 276 277 status_t 278 CallgrindProfileResult::GetImageProfileResult(SharedImage* image, image_id id, 279 ImageProfileResult*& _imageResult) 280 { 281 CallgrindImageProfileResult* result 282 = new(std::nothrow) CallgrindImageProfileResult(image, id); 283 if (result == NULL) 284 return B_NO_MEMORY; 285 286 status_t error = result->Init(); 287 if (error != B_OK) { 288 delete result; 289 return error; 290 } 291 292 _imageResult = result; 293 return B_OK; 294 } 295 296 297 void 298 CallgrindProfileResult::_PrintFunction(FILE* out, 299 CallgrindImageProfileResult* image, int32 functionIndex, bool called) 300 { 301 if (image->OutputIndex() == 0) { 302 // need to print the image name 303 int32 index = fNextImageOutputIndex++; 304 image->SetOutputIndex(index); 305 const char* name = image->GetImage()->Name(); 306 if (name[0] == '/' || strcmp(name, "commpage") == 0) { 307 fprintf(out, 308 "%sob=(%" B_PRId32 ") %s\n", called ? "c" : "", 309 index, name); 310 } else { 311 // add ID to image name 312 fprintf(out, 313 "%sob=(%" B_PRId32 ") %s:%" B_PRId32 "\n", called ? "c" : "", 314 index, name, image->ID()); 315 } 316 } else { 317 // image is already known 318 // TODO: We may not need to print it at all! 319 fprintf(out, 320 "%sob=(%" B_PRId32 ")\n", called ? "c" : "", image->OutputIndex()); 321 } 322 323 CallgrindFunction& function = image->Functions()[functionIndex]; 324 if (function.outputIndex == 0) { 325 // need to print the function name 326 function.outputIndex = fNextFunctionOutputIndex++; 327 fprintf(out, 328 "%sfn=(%" B_PRId32 ") %s\n", called ? "c" : "", 329 function.outputIndex, 330 image->GetImage()->Symbols()[functionIndex]->Name()); 331 } else { 332 // function is already known 333 fprintf(out, 334 "%sfn=(%" B_PRId32 ")\n", called ? "c" : "", function.outputIndex); 335 } 336 } 337