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