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#include <sys/wait.h>
11
12#include <algorithm>
13
14#include <OS.h>
15
16#include "time_stats.h"
17
18
19#define MAX_THREADS	4096
20
21
22struct UsageInfoThreadComparator {
23	inline bool operator()(const thread_info& a, const thread_info& b)
24	{
25		return a.thread < b.thread;
26	}
27};
28
29
30struct UsageInfoTimeComparator {
31	inline bool operator()(const thread_info& a, const thread_info& b)
32	{
33		return a.user_time + a.kernel_time > b.user_time + b.kernel_time;
34	}
35};
36
37
38static int32
39get_usage_infos(thread_info* infos)
40{
41	int32 count = 0;
42
43	int32 teamCookie = 0;
44	team_info teamInfo;
45	while (get_next_team_info(&teamCookie, &teamInfo) == B_OK) {
46		int32 threadCookie = 0;
47		while (get_next_thread_info(teamInfo.team, &threadCookie, &infos[count])
48				== B_OK) {
49			count++;
50		}
51	}
52
53	return count;
54}
55
56
57static pid_t
58run_child(int argc, const char* const* argv)
59{
60	// fork
61	pid_t child = fork();
62	if (child < 0) {
63		fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
64		exit(1);
65	}
66
67	// exec child process
68	if (child == 0) {
69		execvp(argv[0], (char**)argv);
70		exit(1);
71	}
72
73	// wait for child
74	int childStatus;
75	while (wait(&childStatus) < 0);
76
77	return child;
78}
79
80
81void
82do_timing_analysis(int argc, const char* const* argv, bool schedulingAnalysis,
83	int outFD, size_t bufferSize)
84{
85	// gather initial usage info
86	thread_info initialUsage[MAX_THREADS];
87	int32 initialUsageCount = get_usage_infos(initialUsage);
88
89	// exec child process
90	bigtime_t startTime = system_time();
91	pid_t child = run_child(argc, argv);
92
93	// get child usage info
94	bigtime_t endTime = system_time();
95	bigtime_t runTime = endTime - startTime;
96	team_usage_info childUsage;
97	get_team_usage_info(B_CURRENT_TEAM, B_TEAM_USAGE_CHILDREN, &childUsage);
98
99	// gather final usage info
100	thread_info finalUsage[MAX_THREADS];
101	int32 finalUsageCount = get_usage_infos(finalUsage);
102
103	// sort the infos, so we can compare them better
104	std::sort(initialUsage, initialUsage + initialUsageCount,
105		UsageInfoThreadComparator());
106	std::sort(finalUsage, finalUsage + finalUsageCount,
107		UsageInfoThreadComparator());
108
109	// compute results
110
111	thread_info sortedThreads[MAX_THREADS];
112	int32 sortedThreadCount = 0;
113	thread_info goneThreads[MAX_THREADS];
114	int32 goneThreadCount = 0;
115
116	// child
117	sortedThreads[0].thread = child;
118	sortedThreads[0].user_time = childUsage.user_time;
119	sortedThreads[0].kernel_time = childUsage.kernel_time;
120	strlcpy(sortedThreads[0].name, "<child>", sizeof(sortedThreads[0].name));
121	sortedThreadCount++;
122
123	// other threads
124	int32 initialI = 0;
125	int32 finalI = 0;
126	while (initialI < initialUsageCount || finalI < finalUsageCount) {
127		if (initialI >= initialUsageCount
128			|| (finalI < finalUsageCount
129				&& initialUsage[initialI].thread > finalUsage[finalI].thread)) {
130			// new thread
131			memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
132				sizeof(thread_info));
133			sortedThreadCount++;
134			finalI++;
135			continue;
136		}
137
138		if (finalI >= finalUsageCount
139			|| (initialI < initialUsageCount
140				&& initialUsage[initialI].thread < finalUsage[finalI].thread)) {
141			// gone thread
142			memcpy(&goneThreads[goneThreadCount], &initialUsage[initialI],
143				sizeof(thread_info));
144			goneThreadCount++;
145			initialI++;
146			continue;
147		}
148
149		// thread is still there
150		memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
151			sizeof(thread_info));
152		sortedThreads[sortedThreadCount].user_time
153			-= initialUsage[initialI].user_time;
154		sortedThreads[sortedThreadCount].kernel_time
155			-= initialUsage[initialI].kernel_time;
156		sortedThreadCount++;
157		initialI++;
158		finalI++;
159	}
160
161	// sort results
162	std::sort(sortedThreads, sortedThreads + sortedThreadCount,
163		UsageInfoTimeComparator());
164
165	// redirect output, if requested
166	if (outFD >= 0)
167		dup2(outFD, STDOUT_FILENO);
168
169	// print results
170	printf("\nTotal run time: %lld us\n", runTime);
171	printf("Thread time statistics in us:\n\n");
172	printf(" thread  name                                  kernel        user  "
173		"     total    in %%\n");
174	printf("-------------------------------------------------------------------"
175		"------------------\n");
176
177	for (int32 i = 0; i < sortedThreadCount; i++) {
178		const thread_info& info = sortedThreads[i];
179		if (info.user_time + info.kernel_time == 0)
180			break;
181
182		bool highlight = info.thread == child && isatty(STDOUT_FILENO);
183
184		if (highlight)
185			printf("\033[1m");
186
187		bigtime_t total = info.user_time + info.kernel_time;
188		printf("%7ld  %-32s  %10lld  %10lld  %10lld  %6.2f\n", info.thread,
189			info.name, info.kernel_time, info.user_time, total,
190			(double)total / runTime * 100);
191
192		if (highlight)
193			printf("\033[m");
194	}
195
196	for (int32 i = 0; i < goneThreadCount; i++) {
197		const thread_info& info = goneThreads[i];
198		printf("%7ld  %-32s  <gone>\n", info.thread, info.name);
199	}
200
201	if (schedulingAnalysis)
202		do_scheduling_analysis(startTime, endTime, bufferSize);
203}
204