xref: /haiku/src/bin/debug/time_stats/scheduling_analysis.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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