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