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