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_OTHER: 113 sprintf(buffer, "other %p (%s)", object, waitObject->name); 114 break; 115 default: 116 sprintf(buffer, "unknown %p", object); 117 break; 118 } 119 120 return buffer; 121 } 122 123 124 void 125 do_scheduling_analysis(bigtime_t startTime, bigtime_t endTime, 126 size_t bufferSize) 127 { 128 printf("\n"); 129 130 // allocate a chunk of memory for the scheduling analysis 131 void* buffer = malloc(bufferSize); 132 if (buffer == NULL) { 133 fprintf(stderr, "Error: Failed to allocate memory for the scheduling " 134 "analysis.\n"); 135 exit(1); 136 } 137 MemoryDeleter _(buffer); 138 139 // do the scheduling analysis 140 scheduling_analysis analysis; 141 status_t error = _kern_analyze_scheduling(startTime, endTime, buffer, 142 bufferSize, &analysis); 143 if (error != B_OK) { 144 fprintf(stderr, "Error: Scheduling analysis failed: %s\n", 145 strerror(error)); 146 exit(1); 147 } 148 149 // allocate arrays for grouping and sorting the wait objects 150 scheduling_analysis_thread_wait_object** waitObjects 151 = new(std::nothrow) scheduling_analysis_thread_wait_object*[ 152 analysis.thread_wait_object_count]; 153 ArrayDeleter<scheduling_analysis_thread_wait_object*> _2(waitObjects); 154 155 wait_object_group* waitObjectGroups = new(std::nothrow) wait_object_group[ 156 analysis.thread_wait_object_count]; 157 ArrayDeleter<wait_object_group> _3(waitObjectGroups); 158 159 if (waitObjects == NULL || waitObjectGroups == NULL) { 160 fprintf(stderr, "Error: Out of memory\n"); 161 exit(1); 162 } 163 164 printf("scheduling analysis: %lu threads, %llu wait objects, " 165 "%llu thread wait objects\n", analysis.thread_count, 166 analysis.wait_object_count, analysis.thread_wait_object_count); 167 168 // sort the thread by run time 169 std::sort(analysis.threads, analysis.threads + analysis.thread_count, 170 ThreadRunTimeComparator()); 171 172 for (uint32 i = 0; i < analysis.thread_count; i++) { 173 scheduling_analysis_thread* thread = analysis.threads[i]; 174 175 // compute total wait time and prepare the objects for sorting 176 int32 waitObjectCount = 0; 177 bigtime_t waitTime = 0; 178 scheduling_analysis_thread_wait_object* threadWaitObject 179 = thread->wait_objects; 180 while (threadWaitObject != NULL) { 181 waitObjects[waitObjectCount++] = threadWaitObject; 182 waitTime += threadWaitObject->wait_time; 183 threadWaitObject = threadWaitObject->next_in_list; 184 } 185 186 // sort the wait objects by type + name 187 std::sort(waitObjects, waitObjects + waitObjectCount, 188 WaitObjectGroupingComparator()); 189 190 // create the groups 191 wait_object_group* group = NULL; 192 int32 groupCount = 0; 193 for (int32 i = 0; i < waitObjectCount; i++) { 194 scheduling_analysis_thread_wait_object* threadWaitObject 195 = waitObjects[i]; 196 scheduling_analysis_wait_object* waitObject 197 = threadWaitObject->wait_object; 198 199 if (groupCount == 0 || strcmp(waitObject->name, "?") == 0 200 || waitObject->type != group->objects[0]->wait_object->type 201 || strcmp(waitObject->name, 202 group->objects[0]->wait_object->name) != 0) { 203 // create a new group 204 group = &waitObjectGroups[groupCount++]; 205 group->objects = waitObjects + i; 206 group->count = 0; 207 group->wait_time = 0; 208 group->waits = 0; 209 } 210 211 group->count++; 212 group->wait_time += threadWaitObject->wait_time; 213 group->waits += threadWaitObject->waits; 214 } 215 216 // sort the groups by wait time 217 std::sort(waitObjectGroups, waitObjectGroups + groupCount, 218 WaitObjectGroupTimeComparator()); 219 220 printf("\nthread %ld \"%s\":\n", thread->id, thread->name); 221 printf(" run time: %lld us (%lld runs)\n", thread->total_run_time, 222 thread->runs); 223 printf(" wait time: %lld us\n", waitTime); 224 printf(" latencies: %lld us (%lld)\n", thread->total_latency, 225 thread->latencies); 226 printf(" preemptions: %lld us (%lld)\n", thread->total_rerun_time, 227 thread->reruns); 228 printf(" unspecified: %lld us\n", thread->unspecified_wait_time); 229 230 printf(" waited on:\n"); 231 for (int32 i = 0; i < groupCount; i++) { 232 wait_object_group& group = waitObjectGroups[i]; 233 char buffer[1024]; 234 235 if (group.count == 1) { 236 // only one element -- just print it 237 scheduling_analysis_thread_wait_object* threadWaitObject 238 = group.objects[0]; 239 scheduling_analysis_wait_object* waitObject 240 = threadWaitObject->wait_object; 241 wait_object_to_string(waitObject, buffer); 242 printf(" %s: %lld us (%lld)\n", buffer, 243 threadWaitObject->wait_time, threadWaitObject->waits); 244 } else { 245 // sort the wait objects by wait time 246 std::sort(group.objects, group.objects + group.count, 247 WaitObjectTimeComparator()); 248 249 // print the group line 250 wait_object_to_string(group.objects[0]->wait_object, buffer, 251 true); 252 printf(" group %s: %lld us (%lld)\n", buffer, 253 group.wait_time, group.waits); 254 255 // print the wait objects 256 for (int32 k = 0; k < group.count; k++) { 257 scheduling_analysis_thread_wait_object* threadWaitObject 258 = group.objects[k]; 259 scheduling_analysis_wait_object* waitObject 260 = threadWaitObject->wait_object; 261 wait_object_to_string(waitObject, buffer); 262 printf(" %s: %lld us (%lld)\n", buffer, 263 threadWaitObject->wait_time, threadWaitObject->waits); 264 } 265 } 266 } 267 } 268 } 269