1 /* 2 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include <errno.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include <algorithm> 12 13 #include <OS.h> 14 15 #include <AutoDeleter.h> 16 17 #include <scheduler_defs.h> 18 #include <syscalls.h> 19 #include <thread_defs.h> 20 21 #include "time_stats.h" 22 23 24 struct wait_object_group { 25 scheduling_analysis_thread_wait_object** objects; 26 int32 count; 27 bigtime_t wait_time; 28 int64 waits; 29 }; 30 31 32 struct ThreadRunTimeComparator { 33 inline bool operator()(const scheduling_analysis_thread* a, 34 const scheduling_analysis_thread* b) 35 { 36 return a->total_run_time > b->total_run_time; 37 } 38 }; 39 40 41 struct WaitObjectGroupingComparator { 42 inline bool operator()(const scheduling_analysis_thread_wait_object* a, 43 const scheduling_analysis_thread_wait_object* b) 44 { 45 return a->wait_object->type < b->wait_object->type 46 || (a->wait_object->type == b->wait_object->type 47 && strcmp(a->wait_object->name, b->wait_object->name) < 0); 48 } 49 }; 50 51 52 struct WaitObjectTimeComparator { 53 inline bool operator()(const scheduling_analysis_thread_wait_object* a, 54 const scheduling_analysis_thread_wait_object* b) 55 { 56 return a->wait_time > b->wait_time; 57 } 58 }; 59 60 61 struct WaitObjectGroupTimeComparator { 62 inline bool operator()(const wait_object_group& a, 63 const wait_object_group& b) 64 { 65 return a.wait_time > b.wait_time; 66 } 67 }; 68 69 70 static const char* 71 wait_object_to_string(scheduling_analysis_wait_object* waitObject, char* buffer, 72 bool nameOnly = false) 73 { 74 uint32 type = waitObject->type; 75 void* object = waitObject->object; 76 77 switch (type) { 78 case THREAD_BLOCK_TYPE_SEMAPHORE: 79 if (nameOnly) { 80 sprintf(buffer, "sem \"%s\"", waitObject->name); 81 } else { 82 sprintf(buffer, "sem %ld (%s)", (sem_id)(addr_t)object, 83 waitObject->name); 84 } 85 break; 86 case THREAD_BLOCK_TYPE_CONDITION_VARIABLE: 87 if (nameOnly) { 88 sprintf(buffer, "cvar \"%s\"", waitObject->name); 89 } else { 90 sprintf(buffer, "cvar %p (%s %p)", object, waitObject->name, 91 waitObject->referenced_object); 92 } 93 break; 94 case THREAD_BLOCK_TYPE_SNOOZE: 95 strcpy(buffer, "snooze"); 96 break; 97 case THREAD_BLOCK_TYPE_SIGNAL: 98 strcpy(buffer, "signal"); 99 break; 100 case THREAD_BLOCK_TYPE_MUTEX: 101 if (nameOnly) 102 sprintf(buffer, "mutex \"%s\"", waitObject->name); 103 else 104 sprintf(buffer, "mutex %p (%s)", object, waitObject->name); 105 break; 106 case THREAD_BLOCK_TYPE_RW_LOCK: 107 if (nameOnly) 108 sprintf(buffer, "rwlock \"%s\"", waitObject->name); 109 else 110 sprintf(buffer, "rwlock %p (%s)", object, waitObject->name); 111 break; 112 case THREAD_BLOCK_TYPE_USER: 113 strcpy(buffer, "user"); 114 break; 115 case THREAD_BLOCK_TYPE_OTHER: 116 sprintf(buffer, "other %p (%s)", object, waitObject->name); 117 break; 118 default: 119 sprintf(buffer, "unknown %p", object); 120 break; 121 } 122 123 return buffer; 124 } 125 126 127 void 128 do_scheduling_analysis(bigtime_t startTime, bigtime_t endTime, 129 size_t bufferSize) 130 { 131 printf("\n"); 132 133 // allocate a chunk of memory for the scheduling analysis 134 void* buffer = malloc(bufferSize); 135 if (buffer == NULL) { 136 fprintf(stderr, "Error: Failed to allocate memory for the scheduling " 137 "analysis.\n"); 138 exit(1); 139 } 140 MemoryDeleter _(buffer); 141 142 // do the scheduling analysis 143 scheduling_analysis analysis; 144 status_t error = _kern_analyze_scheduling(startTime, endTime, buffer, 145 bufferSize, &analysis); 146 if (error != B_OK) { 147 fprintf(stderr, "Error: Scheduling analysis failed: %s\n", 148 strerror(error)); 149 exit(1); 150 } 151 152 // allocate arrays for grouping and sorting the wait objects 153 scheduling_analysis_thread_wait_object** waitObjects 154 = new(std::nothrow) scheduling_analysis_thread_wait_object*[ 155 analysis.thread_wait_object_count]; 156 ArrayDeleter<scheduling_analysis_thread_wait_object*> _2(waitObjects); 157 158 wait_object_group* waitObjectGroups = new(std::nothrow) wait_object_group[ 159 analysis.thread_wait_object_count]; 160 ArrayDeleter<wait_object_group> _3(waitObjectGroups); 161 162 if (waitObjects == NULL || waitObjectGroups == NULL) { 163 fprintf(stderr, "Error: Out of memory\n"); 164 exit(1); 165 } 166 167 printf("scheduling analysis: %lu threads, %llu wait objects, " 168 "%llu thread wait objects\n", analysis.thread_count, 169 analysis.wait_object_count, analysis.thread_wait_object_count); 170 171 // sort the thread by run time 172 std::sort(analysis.threads, analysis.threads + analysis.thread_count, 173 ThreadRunTimeComparator()); 174 175 for (uint32 i = 0; i < analysis.thread_count; i++) { 176 scheduling_analysis_thread* thread = analysis.threads[i]; 177 178 // compute total wait time and prepare the objects for sorting 179 int32 waitObjectCount = 0; 180 bigtime_t waitTime = 0; 181 scheduling_analysis_thread_wait_object* threadWaitObject 182 = thread->wait_objects; 183 while (threadWaitObject != NULL) { 184 waitObjects[waitObjectCount++] = threadWaitObject; 185 waitTime += threadWaitObject->wait_time; 186 threadWaitObject = threadWaitObject->next_in_list; 187 } 188 189 // sort the wait objects by type + name 190 std::sort(waitObjects, waitObjects + waitObjectCount, 191 WaitObjectGroupingComparator()); 192 193 // create the groups 194 wait_object_group* group = NULL; 195 int32 groupCount = 0; 196 for (int32 i = 0; i < waitObjectCount; i++) { 197 scheduling_analysis_thread_wait_object* threadWaitObject 198 = waitObjects[i]; 199 scheduling_analysis_wait_object* waitObject 200 = threadWaitObject->wait_object; 201 202 if (groupCount == 0 || strcmp(waitObject->name, "?") == 0 203 || waitObject->type != group->objects[0]->wait_object->type 204 || strcmp(waitObject->name, 205 group->objects[0]->wait_object->name) != 0) { 206 // create a new group 207 group = &waitObjectGroups[groupCount++]; 208 group->objects = waitObjects + i; 209 group->count = 0; 210 group->wait_time = 0; 211 group->waits = 0; 212 } 213 214 group->count++; 215 group->wait_time += threadWaitObject->wait_time; 216 group->waits += threadWaitObject->waits; 217 } 218 219 // sort the groups by wait time 220 std::sort(waitObjectGroups, waitObjectGroups + groupCount, 221 WaitObjectGroupTimeComparator()); 222 223 printf("\nthread %ld \"%s\":\n", thread->id, thread->name); 224 printf(" run time: %lld us (%lld runs)\n", thread->total_run_time, 225 thread->runs); 226 printf(" wait time: %lld us\n", waitTime); 227 printf(" latencies: %lld us (%lld)\n", thread->total_latency, 228 thread->latencies); 229 printf(" preemptions: %lld us (%lld)\n", thread->total_rerun_time, 230 thread->reruns); 231 printf(" unspecified: %lld us\n", thread->unspecified_wait_time); 232 233 printf(" waited on:\n"); 234 for (int32 i = 0; i < groupCount; i++) { 235 wait_object_group& group = waitObjectGroups[i]; 236 char buffer[1024]; 237 238 if (group.count == 1) { 239 // only one element -- just print it 240 scheduling_analysis_thread_wait_object* threadWaitObject 241 = group.objects[0]; 242 scheduling_analysis_wait_object* waitObject 243 = threadWaitObject->wait_object; 244 wait_object_to_string(waitObject, buffer); 245 printf(" %s: %lld us (%lld)\n", buffer, 246 threadWaitObject->wait_time, threadWaitObject->waits); 247 } else { 248 // sort the wait objects by wait time 249 std::sort(group.objects, group.objects + group.count, 250 WaitObjectTimeComparator()); 251 252 // print the group line 253 wait_object_to_string(group.objects[0]->wait_object, buffer, 254 true); 255 printf(" group %s: %lld us (%lld)\n", buffer, 256 group.wait_time, group.waits); 257 258 // print the wait objects 259 for (int32 k = 0; k < group.count; k++) { 260 scheduling_analysis_thread_wait_object* threadWaitObject 261 = group.objects[k]; 262 scheduling_analysis_wait_object* waitObject 263 = threadWaitObject->wait_object; 264 wait_object_to_string(waitObject, buffer); 265 printf(" %s: %lld us (%lld)\n", buffer, 266 threadWaitObject->wait_time, threadWaitObject->waits); 267 } 268 } 269 } 270 } 271 } 272