* Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
* Distributed under the terms of the MIT License.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <OS.h>
#include <AutoDeleter.h>
#include <scheduler_defs.h>
#include <syscalls.h>
#include <thread_defs.h>
#include "time_stats.h"
struct wait_object_group {
scheduling_analysis_thread_wait_object** objects;
int32 count;
bigtime_t wait_time;
int64 waits;
};
struct ThreadRunTimeComparator {
inline bool operator()(const scheduling_analysis_thread* a,
const scheduling_analysis_thread* b)
{
return a->total_run_time > b->total_run_time;
}
};
struct WaitObjectGroupingComparator {
inline bool operator()(const scheduling_analysis_thread_wait_object* a,
const scheduling_analysis_thread_wait_object* b)
{
return a->wait_object->type < b->wait_object->type
|| (a->wait_object->type == b->wait_object->type
&& strcmp(a->wait_object->name, b->wait_object->name) < 0);
}
};
struct WaitObjectTimeComparator {
inline bool operator()(const scheduling_analysis_thread_wait_object* a,
const scheduling_analysis_thread_wait_object* b)
{
return a->wait_time > b->wait_time;
}
};
struct WaitObjectGroupTimeComparator {
inline bool operator()(const wait_object_group& a,
const wait_object_group& b)
{
return a.wait_time > b.wait_time;
}
};
static const char*
wait_object_to_string(scheduling_analysis_wait_object* waitObject, char* buffer,
bool nameOnly = false)
{
uint32 type = waitObject->type;
void* object = waitObject->object;
switch (type) {
case THREAD_BLOCK_TYPE_SEMAPHORE:
if (nameOnly) {
sprintf(buffer, "sem \"%s\"", waitObject->name);
} else {
sprintf(buffer, "sem %ld (%s)", (sem_id)(addr_t)object,
waitObject->name);
}
break;
case THREAD_BLOCK_TYPE_CONDITION_VARIABLE:
if (nameOnly) {
sprintf(buffer, "cvar \"%s\"", waitObject->name);
} else {
sprintf(buffer, "cvar %p (%s %p)", object, waitObject->name,
waitObject->referenced_object);
}
break;
case THREAD_BLOCK_TYPE_SNOOZE:
strcpy(buffer, "snooze");
break;
case THREAD_BLOCK_TYPE_SIGNAL:
strcpy(buffer, "signal");
break;
case THREAD_BLOCK_TYPE_MUTEX:
if (nameOnly)
sprintf(buffer, "mutex \"%s\"", waitObject->name);
else
sprintf(buffer, "mutex %p (%s)", object, waitObject->name);
break;
case THREAD_BLOCK_TYPE_RW_LOCK:
if (nameOnly)
sprintf(buffer, "rwlock \"%s\"", waitObject->name);
else
sprintf(buffer, "rwlock %p (%s)", object, waitObject->name);
break;
case THREAD_BLOCK_TYPE_USER:
strcpy(buffer, "user");
break;
case THREAD_BLOCK_TYPE_OTHER:
sprintf(buffer, "other %p (%s)", object, waitObject->name);
break;
case THREAD_BLOCK_TYPE_OTHER_OBJECT:
sprintf(buffer, "other object %p", object);
break;
default:
sprintf(buffer, "unknown %p", object);
break;
}
return buffer;
}
void
do_scheduling_analysis(bigtime_t startTime, bigtime_t endTime,
size_t bufferSize)
{
printf("\n");
void* buffer = malloc(bufferSize);
if (buffer == NULL) {
fprintf(stderr, "Error: Failed to allocate memory for the scheduling "
"analysis.\n");
exit(1);
}
MemoryDeleter _(buffer);
scheduling_analysis analysis;
status_t error = _kern_analyze_scheduling(startTime, endTime, buffer,
bufferSize, &analysis);
if (error != B_OK) {
fprintf(stderr, "Error: Scheduling analysis failed: %s\n",
strerror(error));
exit(1);
}
scheduling_analysis_thread_wait_object** waitObjects
= new(std::nothrow) scheduling_analysis_thread_wait_object*[
analysis.thread_wait_object_count];
ArrayDeleter<scheduling_analysis_thread_wait_object*> _2(waitObjects);
wait_object_group* waitObjectGroups = new(std::nothrow) wait_object_group[
analysis.thread_wait_object_count];
ArrayDeleter<wait_object_group> _3(waitObjectGroups);
if (waitObjects == NULL || waitObjectGroups == NULL) {
fprintf(stderr, "Error: Out of memory\n");
exit(1);
}
printf("scheduling analysis: %lu threads, %llu wait objects, "
"%llu thread wait objects\n", analysis.thread_count,
analysis.wait_object_count, analysis.thread_wait_object_count);
std::sort(analysis.threads, analysis.threads + analysis.thread_count,
ThreadRunTimeComparator());
for (uint32 i = 0; i < analysis.thread_count; i++) {
scheduling_analysis_thread* thread = analysis.threads[i];
int32 waitObjectCount = 0;
bigtime_t waitTime = 0;
scheduling_analysis_thread_wait_object* threadWaitObject
= thread->wait_objects;
while (threadWaitObject != NULL) {
waitObjects[waitObjectCount++] = threadWaitObject;
waitTime += threadWaitObject->wait_time;
threadWaitObject = threadWaitObject->next_in_list;
}
std::sort(waitObjects, waitObjects + waitObjectCount,
WaitObjectGroupingComparator());
wait_object_group* group = NULL;
int32 groupCount = 0;
for (int32 i = 0; i < waitObjectCount; i++) {
scheduling_analysis_thread_wait_object* threadWaitObject
= waitObjects[i];
scheduling_analysis_wait_object* waitObject
= threadWaitObject->wait_object;
if (groupCount == 0 || strcmp(waitObject->name, "?") == 0
|| waitObject->type != group->objects[0]->wait_object->type
|| strcmp(waitObject->name,
group->objects[0]->wait_object->name) != 0) {
group = &waitObjectGroups[groupCount++];
group->objects = waitObjects + i;
group->count = 0;
group->wait_time = 0;
group->waits = 0;
}
group->count++;
group->wait_time += threadWaitObject->wait_time;
group->waits += threadWaitObject->waits;
}
std::sort(waitObjectGroups, waitObjectGroups + groupCount,
WaitObjectGroupTimeComparator());
printf("\nthread %ld \"%s\":\n", thread->id, thread->name);
printf(" run time: %lld us (%lld runs)\n", thread->total_run_time,
thread->runs);
printf(" wait time: %lld us\n", waitTime);
printf(" latencies: %lld us (%lld)\n", thread->total_latency,
thread->latencies);
printf(" preemptions: %lld us (%lld)\n", thread->total_rerun_time,
thread->reruns);
printf(" unspecified: %lld us\n", thread->unspecified_wait_time);
printf(" waited on:\n");
for (int32 i = 0; i < groupCount; i++) {
wait_object_group& group = waitObjectGroups[i];
char buffer[1024];
if (group.count == 1) {
scheduling_analysis_thread_wait_object* threadWaitObject
= group.objects[0];
scheduling_analysis_wait_object* waitObject
= threadWaitObject->wait_object;
wait_object_to_string(waitObject, buffer);
printf(" %s: %lld us (%lld)\n", buffer,
threadWaitObject->wait_time, threadWaitObject->waits);
} else {
std::sort(group.objects, group.objects + group.count,
WaitObjectTimeComparator());
wait_object_to_string(group.objects[0]->wait_object, buffer,
true);
printf(" group %s: %lld us (%lld)\n", buffer,
group.wait_time, group.waits);
for (int32 k = 0; k < group.count; k++) {
scheduling_analysis_thread_wait_object* threadWaitObject
= group.objects[k];
scheduling_analysis_wait_object* waitObject
= threadWaitObject->wait_object;
wait_object_to_string(waitObject, buffer);
printf(" %s: %lld us (%lld)\n", buffer,
threadWaitObject->wait_time, threadWaitObject->waits);
}
}
}
}
}