Split into different source files. No functional change.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30237 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
SubDir HAIKU_TOP src bin debug time_stats ;
|
||||||
|
|
||||||
|
UsePrivateHeaders kernel ;
|
||||||
|
UsePrivateHeaders libroot ;
|
||||||
|
UsePrivateHeaders shared ;
|
||||||
|
UsePrivateSystemHeaders ;
|
||||||
|
|
||||||
|
SubDirHdrs [ FDirName $(SUBDIR) $(DOTDOT) ] ;
|
||||||
|
|
||||||
|
BinCommand time_stats
|
||||||
|
:
|
||||||
|
scheduling_analysis.cpp
|
||||||
|
time_stats.cpp
|
||||||
|
timing_analysis.cpp
|
||||||
|
:
|
||||||
|
$(TARGET_LIBSTDC++)
|
||||||
|
;
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008-2009, Ingo Weinhold, [email protected].
|
||||||
|
* 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_OTHER:
|
||||||
|
sprintf(buffer, "other %p (%s)", object, waitObject->name);
|
||||||
|
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");
|
||||||
|
|
||||||
|
// allocate a chunk of memory for the scheduling analysis
|
||||||
|
void* buffer = malloc(bufferSize);
|
||||||
|
if (buffer == NULL) {
|
||||||
|
fprintf(stderr, "Error: Failed to allocate memory for the scheduling "
|
||||||
|
"analysis.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
MemoryDeleter _(buffer);
|
||||||
|
|
||||||
|
// do the scheduling analysis
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate arrays for grouping and sorting the wait objects
|
||||||
|
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);
|
||||||
|
|
||||||
|
// sort the thread by run time
|
||||||
|
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];
|
||||||
|
|
||||||
|
// compute total wait time and prepare the objects for sorting
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the wait objects by type + name
|
||||||
|
std::sort(waitObjects, waitObjects + waitObjectCount,
|
||||||
|
WaitObjectGroupingComparator());
|
||||||
|
|
||||||
|
// create the groups
|
||||||
|
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) {
|
||||||
|
// create a new group
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the groups by wait time
|
||||||
|
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) {
|
||||||
|
// only one element -- just print it
|
||||||
|
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 {
|
||||||
|
// sort the wait objects by wait time
|
||||||
|
std::sort(group.objects, group.objects + group.count,
|
||||||
|
WaitObjectTimeComparator());
|
||||||
|
|
||||||
|
// print the group line
|
||||||
|
wait_object_to_string(group.objects[0]->wait_object, buffer,
|
||||||
|
true);
|
||||||
|
printf(" group %s: %lld us (%lld)\n", buffer,
|
||||||
|
group.wait_time, group.waits);
|
||||||
|
|
||||||
|
// print the wait objects
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,21 +10,12 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
|
|
||||||
#include <AutoDeleter.h>
|
#include "time_stats.h"
|
||||||
|
|
||||||
#include <scheduler_defs.h>
|
|
||||||
#include <syscalls.h>
|
|
||||||
#include <thread_defs.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define MAX_THREADS 4096
|
|
||||||
|
|
||||||
#define SCHEDULING_ANALYSIS_BUFFER_SIZE 10 * 1024 * 1024
|
#define SCHEDULING_ANALYSIS_BUFFER_SIZE 10 * 1024 * 1024
|
||||||
|
|
||||||
@@ -55,436 +46,6 @@ print_usage_and_exit(bool error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct UsageInfoThreadComparator {
|
|
||||||
inline bool operator()(const thread_info& a, const thread_info& b)
|
|
||||||
{
|
|
||||||
return a.thread < b.thread;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct UsageInfoTimeComparator {
|
|
||||||
inline bool operator()(const thread_info& a, const thread_info& b)
|
|
||||||
{
|
|
||||||
return a.user_time + a.kernel_time > b.user_time + b.kernel_time;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
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 int32
|
|
||||||
get_usage_infos(thread_info* infos)
|
|
||||||
{
|
|
||||||
int32 count = 0;
|
|
||||||
|
|
||||||
int32 teamCookie = 0;
|
|
||||||
team_info teamInfo;
|
|
||||||
while (get_next_team_info(&teamCookie, &teamInfo) == B_OK) {
|
|
||||||
int32 threadCookie = 0;
|
|
||||||
while (get_next_thread_info(teamInfo.team, &threadCookie, &infos[count])
|
|
||||||
== B_OK) {
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static pid_t
|
|
||||||
run_child(int argc, const char* const* argv)
|
|
||||||
{
|
|
||||||
// fork
|
|
||||||
pid_t child = fork();
|
|
||||||
if (child < 0) {
|
|
||||||
fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// exec child process
|
|
||||||
if (child == 0) {
|
|
||||||
execvp(argv[0], (char**)argv);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait for child
|
|
||||||
int childStatus;
|
|
||||||
while (wait(&childStatus) < 0);
|
|
||||||
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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_OTHER:
|
|
||||||
sprintf(buffer, "other %p (%s)", object, waitObject->name);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
sprintf(buffer, "unknown %p", object);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
do_scheduling_analysis(bigtime_t startTime, bigtime_t endTime,
|
|
||||||
size_t bufferSize)
|
|
||||||
{
|
|
||||||
printf("\n");
|
|
||||||
|
|
||||||
// allocate a chunk of memory for the scheduling analysis
|
|
||||||
void* buffer = malloc(bufferSize);
|
|
||||||
if (buffer == NULL) {
|
|
||||||
fprintf(stderr, "Error: Failed to allocate memory for the scheduling "
|
|
||||||
"analysis.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
MemoryDeleter _(buffer);
|
|
||||||
|
|
||||||
// do the scheduling analysis
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// allocate arrays for grouping and sorting the wait objects
|
|
||||||
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);
|
|
||||||
|
|
||||||
// sort the thread by run time
|
|
||||||
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];
|
|
||||||
|
|
||||||
// compute total wait time and prepare the objects for sorting
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort the wait objects by type + name
|
|
||||||
std::sort(waitObjects, waitObjects + waitObjectCount,
|
|
||||||
WaitObjectGroupingComparator());
|
|
||||||
|
|
||||||
// create the groups
|
|
||||||
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) {
|
|
||||||
// create a new group
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort the groups by wait time
|
|
||||||
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) {
|
|
||||||
// only one element -- just print it
|
|
||||||
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 {
|
|
||||||
// sort the wait objects by wait time
|
|
||||||
std::sort(group.objects, group.objects + group.count,
|
|
||||||
WaitObjectTimeComparator());
|
|
||||||
|
|
||||||
// print the group line
|
|
||||||
wait_object_to_string(group.objects[0]->wait_object, buffer,
|
|
||||||
true);
|
|
||||||
printf(" group %s: %lld us (%lld)\n", buffer,
|
|
||||||
group.wait_time, group.waits);
|
|
||||||
|
|
||||||
// print the wait objects
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
do_timing_analysis(int argc, const char* const* argv, bool schedulingAnalysis,
|
|
||||||
int outFD, size_t bufferSize)
|
|
||||||
{
|
|
||||||
// gather initial usage info
|
|
||||||
thread_info initialUsage[MAX_THREADS];
|
|
||||||
int32 initialUsageCount = get_usage_infos(initialUsage);
|
|
||||||
|
|
||||||
// exec child process
|
|
||||||
bigtime_t startTime = system_time();
|
|
||||||
pid_t child = run_child(argc, argv);
|
|
||||||
|
|
||||||
// get child usage info
|
|
||||||
bigtime_t endTime = system_time();
|
|
||||||
bigtime_t runTime = endTime - startTime;
|
|
||||||
team_usage_info childUsage;
|
|
||||||
get_team_usage_info(B_CURRENT_TEAM, B_TEAM_USAGE_CHILDREN, &childUsage);
|
|
||||||
|
|
||||||
// gather final usage info
|
|
||||||
thread_info finalUsage[MAX_THREADS];
|
|
||||||
int32 finalUsageCount = get_usage_infos(finalUsage);
|
|
||||||
|
|
||||||
// sort the infos, so we can compare them better
|
|
||||||
std::sort(initialUsage, initialUsage + initialUsageCount,
|
|
||||||
UsageInfoThreadComparator());
|
|
||||||
std::sort(finalUsage, finalUsage + finalUsageCount,
|
|
||||||
UsageInfoThreadComparator());
|
|
||||||
|
|
||||||
// compute results
|
|
||||||
|
|
||||||
thread_info sortedThreads[MAX_THREADS];
|
|
||||||
int32 sortedThreadCount = 0;
|
|
||||||
thread_info goneThreads[MAX_THREADS];
|
|
||||||
int32 goneThreadCount = 0;
|
|
||||||
|
|
||||||
// child
|
|
||||||
sortedThreads[0].thread = child;
|
|
||||||
sortedThreads[0].user_time = childUsage.user_time;
|
|
||||||
sortedThreads[0].kernel_time = childUsage.kernel_time;
|
|
||||||
strlcpy(sortedThreads[0].name, "<child>", sizeof(sortedThreads[0].name));
|
|
||||||
sortedThreadCount++;
|
|
||||||
|
|
||||||
// other threads
|
|
||||||
int32 initialI = 0;
|
|
||||||
int32 finalI = 0;
|
|
||||||
while (initialI < initialUsageCount || finalI < finalUsageCount) {
|
|
||||||
if (initialI >= initialUsageCount
|
|
||||||
|| (finalI < finalUsageCount
|
|
||||||
&& initialUsage[initialI].thread > finalUsage[finalI].thread)) {
|
|
||||||
// new thread
|
|
||||||
memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
|
|
||||||
sizeof(thread_info));
|
|
||||||
sortedThreadCount++;
|
|
||||||
finalI++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (finalI >= finalUsageCount
|
|
||||||
|| (initialI < initialUsageCount
|
|
||||||
&& initialUsage[initialI].thread < finalUsage[finalI].thread)) {
|
|
||||||
// gone thread
|
|
||||||
memcpy(&goneThreads[goneThreadCount], &initialUsage[initialI],
|
|
||||||
sizeof(thread_info));
|
|
||||||
goneThreadCount++;
|
|
||||||
initialI++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// thread is still there
|
|
||||||
memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
|
|
||||||
sizeof(thread_info));
|
|
||||||
sortedThreads[sortedThreadCount].user_time
|
|
||||||
-= initialUsage[initialI].user_time;
|
|
||||||
sortedThreads[sortedThreadCount].kernel_time
|
|
||||||
-= initialUsage[initialI].kernel_time;
|
|
||||||
sortedThreadCount++;
|
|
||||||
initialI++;
|
|
||||||
finalI++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort results
|
|
||||||
std::sort(sortedThreads, sortedThreads + sortedThreadCount,
|
|
||||||
UsageInfoTimeComparator());
|
|
||||||
|
|
||||||
// redirect output, if requested
|
|
||||||
if (outFD >= 0)
|
|
||||||
dup2(outFD, STDOUT_FILENO);
|
|
||||||
|
|
||||||
// print results
|
|
||||||
printf("\nTotal run time: %lld us\n", runTime);
|
|
||||||
printf("Thread time statistics in us:\n\n");
|
|
||||||
printf(" thread name kernel user "
|
|
||||||
" total in %%\n");
|
|
||||||
printf("-------------------------------------------------------------------"
|
|
||||||
"------------------\n");
|
|
||||||
|
|
||||||
for (int32 i = 0; i < sortedThreadCount; i++) {
|
|
||||||
const thread_info& info = sortedThreads[i];
|
|
||||||
if (info.user_time + info.kernel_time == 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
bool highlight = info.thread == child && isatty(STDOUT_FILENO);
|
|
||||||
|
|
||||||
if (highlight)
|
|
||||||
printf("\033[1m");
|
|
||||||
|
|
||||||
bigtime_t total = info.user_time + info.kernel_time;
|
|
||||||
printf("%7ld %-32s %10lld %10lld %10lld %6.2f\n", info.thread,
|
|
||||||
info.name, info.kernel_time, info.user_time, total,
|
|
||||||
(double)total / runTime * 100);
|
|
||||||
|
|
||||||
if (highlight)
|
|
||||||
printf("\033[m");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int32 i = 0; i < goneThreadCount; i++) {
|
|
||||||
const thread_info& info = goneThreads[i];
|
|
||||||
printf("%7ld %-32s <gone>\n", info.thread, info.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (schedulingAnalysis)
|
|
||||||
do_scheduling_analysis(startTime, endTime, bufferSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, const char* const* argv)
|
main(int argc, const char* const* argv)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
#ifndef TIME_STATS_H
|
||||||
|
#define TIME_STATS_H
|
||||||
|
|
||||||
|
#include <SupportDefs.h>
|
||||||
|
|
||||||
|
|
||||||
|
void do_scheduling_analysis(bigtime_t startTime, bigtime_t endTime,
|
||||||
|
size_t bufferSize);
|
||||||
|
void do_timing_analysis(int argc, const char* const* argv,
|
||||||
|
bool schedulingAnalysis, int outFD, size_t bufferSize);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // TIME_STATS_H
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2008-2009, Ingo Weinhold, [email protected].
|
||||||
|
* Distributed under the terms of the MIT License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <OS.h>
|
||||||
|
|
||||||
|
#include "time_stats.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_THREADS 4096
|
||||||
|
|
||||||
|
|
||||||
|
struct UsageInfoThreadComparator {
|
||||||
|
inline bool operator()(const thread_info& a, const thread_info& b)
|
||||||
|
{
|
||||||
|
return a.thread < b.thread;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct UsageInfoTimeComparator {
|
||||||
|
inline bool operator()(const thread_info& a, const thread_info& b)
|
||||||
|
{
|
||||||
|
return a.user_time + a.kernel_time > b.user_time + b.kernel_time;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static int32
|
||||||
|
get_usage_infos(thread_info* infos)
|
||||||
|
{
|
||||||
|
int32 count = 0;
|
||||||
|
|
||||||
|
int32 teamCookie = 0;
|
||||||
|
team_info teamInfo;
|
||||||
|
while (get_next_team_info(&teamCookie, &teamInfo) == B_OK) {
|
||||||
|
int32 threadCookie = 0;
|
||||||
|
while (get_next_thread_info(teamInfo.team, &threadCookie, &infos[count])
|
||||||
|
== B_OK) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static pid_t
|
||||||
|
run_child(int argc, const char* const* argv)
|
||||||
|
{
|
||||||
|
// fork
|
||||||
|
pid_t child = fork();
|
||||||
|
if (child < 0) {
|
||||||
|
fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// exec child process
|
||||||
|
if (child == 0) {
|
||||||
|
execvp(argv[0], (char**)argv);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for child
|
||||||
|
int childStatus;
|
||||||
|
while (wait(&childStatus) < 0);
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
do_timing_analysis(int argc, const char* const* argv, bool schedulingAnalysis,
|
||||||
|
int outFD, size_t bufferSize)
|
||||||
|
{
|
||||||
|
// gather initial usage info
|
||||||
|
thread_info initialUsage[MAX_THREADS];
|
||||||
|
int32 initialUsageCount = get_usage_infos(initialUsage);
|
||||||
|
|
||||||
|
// exec child process
|
||||||
|
bigtime_t startTime = system_time();
|
||||||
|
pid_t child = run_child(argc, argv);
|
||||||
|
|
||||||
|
// get child usage info
|
||||||
|
bigtime_t endTime = system_time();
|
||||||
|
bigtime_t runTime = endTime - startTime;
|
||||||
|
team_usage_info childUsage;
|
||||||
|
get_team_usage_info(B_CURRENT_TEAM, B_TEAM_USAGE_CHILDREN, &childUsage);
|
||||||
|
|
||||||
|
// gather final usage info
|
||||||
|
thread_info finalUsage[MAX_THREADS];
|
||||||
|
int32 finalUsageCount = get_usage_infos(finalUsage);
|
||||||
|
|
||||||
|
// sort the infos, so we can compare them better
|
||||||
|
std::sort(initialUsage, initialUsage + initialUsageCount,
|
||||||
|
UsageInfoThreadComparator());
|
||||||
|
std::sort(finalUsage, finalUsage + finalUsageCount,
|
||||||
|
UsageInfoThreadComparator());
|
||||||
|
|
||||||
|
// compute results
|
||||||
|
|
||||||
|
thread_info sortedThreads[MAX_THREADS];
|
||||||
|
int32 sortedThreadCount = 0;
|
||||||
|
thread_info goneThreads[MAX_THREADS];
|
||||||
|
int32 goneThreadCount = 0;
|
||||||
|
|
||||||
|
// child
|
||||||
|
sortedThreads[0].thread = child;
|
||||||
|
sortedThreads[0].user_time = childUsage.user_time;
|
||||||
|
sortedThreads[0].kernel_time = childUsage.kernel_time;
|
||||||
|
strlcpy(sortedThreads[0].name, "<child>", sizeof(sortedThreads[0].name));
|
||||||
|
sortedThreadCount++;
|
||||||
|
|
||||||
|
// other threads
|
||||||
|
int32 initialI = 0;
|
||||||
|
int32 finalI = 0;
|
||||||
|
while (initialI < initialUsageCount || finalI < finalUsageCount) {
|
||||||
|
if (initialI >= initialUsageCount
|
||||||
|
|| (finalI < finalUsageCount
|
||||||
|
&& initialUsage[initialI].thread > finalUsage[finalI].thread)) {
|
||||||
|
// new thread
|
||||||
|
memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
|
||||||
|
sizeof(thread_info));
|
||||||
|
sortedThreadCount++;
|
||||||
|
finalI++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (finalI >= finalUsageCount
|
||||||
|
|| (initialI < initialUsageCount
|
||||||
|
&& initialUsage[initialI].thread < finalUsage[finalI].thread)) {
|
||||||
|
// gone thread
|
||||||
|
memcpy(&goneThreads[goneThreadCount], &initialUsage[initialI],
|
||||||
|
sizeof(thread_info));
|
||||||
|
goneThreadCount++;
|
||||||
|
initialI++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// thread is still there
|
||||||
|
memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
|
||||||
|
sizeof(thread_info));
|
||||||
|
sortedThreads[sortedThreadCount].user_time
|
||||||
|
-= initialUsage[initialI].user_time;
|
||||||
|
sortedThreads[sortedThreadCount].kernel_time
|
||||||
|
-= initialUsage[initialI].kernel_time;
|
||||||
|
sortedThreadCount++;
|
||||||
|
initialI++;
|
||||||
|
finalI++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort results
|
||||||
|
std::sort(sortedThreads, sortedThreads + sortedThreadCount,
|
||||||
|
UsageInfoTimeComparator());
|
||||||
|
|
||||||
|
// redirect output, if requested
|
||||||
|
if (outFD >= 0)
|
||||||
|
dup2(outFD, STDOUT_FILENO);
|
||||||
|
|
||||||
|
// print results
|
||||||
|
printf("\nTotal run time: %lld us\n", runTime);
|
||||||
|
printf("Thread time statistics in us:\n\n");
|
||||||
|
printf(" thread name kernel user "
|
||||||
|
" total in %%\n");
|
||||||
|
printf("-------------------------------------------------------------------"
|
||||||
|
"------------------\n");
|
||||||
|
|
||||||
|
for (int32 i = 0; i < sortedThreadCount; i++) {
|
||||||
|
const thread_info& info = sortedThreads[i];
|
||||||
|
if (info.user_time + info.kernel_time == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
bool highlight = info.thread == child && isatty(STDOUT_FILENO);
|
||||||
|
|
||||||
|
if (highlight)
|
||||||
|
printf("\033[1m");
|
||||||
|
|
||||||
|
bigtime_t total = info.user_time + info.kernel_time;
|
||||||
|
printf("%7ld %-32s %10lld %10lld %10lld %6.2f\n", info.thread,
|
||||||
|
info.name, info.kernel_time, info.user_time, total,
|
||||||
|
(double)total / runTime * 100);
|
||||||
|
|
||||||
|
if (highlight)
|
||||||
|
printf("\033[m");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32 i = 0; i < goneThreadCount; i++) {
|
||||||
|
const thread_info& info = goneThreads[i];
|
||||||
|
printf("%7ld %-32s <gone>\n", info.thread, info.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (schedulingAnalysis)
|
||||||
|
do_scheduling_analysis(startTime, endTime, bufferSize);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user