144521466SIngo Weinhold/*
244521466SIngo Weinhold * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
344521466SIngo Weinhold * Distributed under the terms of the MIT License.
444521466SIngo Weinhold */
544521466SIngo Weinhold
644521466SIngo Weinhold#include <errno.h>
744521466SIngo Weinhold#include <stdio.h>
844521466SIngo Weinhold#include <stdlib.h>
944521466SIngo Weinhold#include <string.h>
1044521466SIngo Weinhold#include <sys/wait.h>
1144521466SIngo Weinhold
1244521466SIngo Weinhold#include <algorithm>
1344521466SIngo Weinhold
1444521466SIngo Weinhold#include <OS.h>
1544521466SIngo Weinhold
1644521466SIngo Weinhold#include "time_stats.h"
1744521466SIngo Weinhold
1844521466SIngo Weinhold
1944521466SIngo Weinhold#define MAX_THREADS	4096
2044521466SIngo Weinhold
2144521466SIngo Weinhold
2244521466SIngo Weinholdstruct UsageInfoThreadComparator {
2344521466SIngo Weinhold	inline bool operator()(const thread_info& a, const thread_info& b)
2444521466SIngo Weinhold	{
2544521466SIngo Weinhold		return a.thread < b.thread;
2644521466SIngo Weinhold	}
2744521466SIngo Weinhold};
2844521466SIngo Weinhold
2944521466SIngo Weinhold
3044521466SIngo Weinholdstruct UsageInfoTimeComparator {
3144521466SIngo Weinhold	inline bool operator()(const thread_info& a, const thread_info& b)
3244521466SIngo Weinhold	{
3344521466SIngo Weinhold		return a.user_time + a.kernel_time > b.user_time + b.kernel_time;
3444521466SIngo Weinhold	}
3544521466SIngo Weinhold};
3644521466SIngo Weinhold
3744521466SIngo Weinhold
3844521466SIngo Weinholdstatic int32
3944521466SIngo Weinholdget_usage_infos(thread_info* infos)
4044521466SIngo Weinhold{
4144521466SIngo Weinhold	int32 count = 0;
4244521466SIngo Weinhold
4344521466SIngo Weinhold	int32 teamCookie = 0;
4444521466SIngo Weinhold	team_info teamInfo;
4544521466SIngo Weinhold	while (get_next_team_info(&teamCookie, &teamInfo) == B_OK) {
4644521466SIngo Weinhold		int32 threadCookie = 0;
4744521466SIngo Weinhold		while (get_next_thread_info(teamInfo.team, &threadCookie, &infos[count])
4844521466SIngo Weinhold				== B_OK) {
4944521466SIngo Weinhold			count++;
5044521466SIngo Weinhold		}
5144521466SIngo Weinhold	}
5244521466SIngo Weinhold
5344521466SIngo Weinhold	return count;
5444521466SIngo Weinhold}
5544521466SIngo Weinhold
5644521466SIngo Weinhold
5744521466SIngo Weinholdstatic pid_t
5844521466SIngo Weinholdrun_child(int argc, const char* const* argv)
5944521466SIngo Weinhold{
6044521466SIngo Weinhold	// fork
6144521466SIngo Weinhold	pid_t child = fork();
6244521466SIngo Weinhold	if (child < 0) {
6344521466SIngo Weinhold		fprintf(stderr, "Error: fork() failed: %s\n", strerror(errno));
6444521466SIngo Weinhold		exit(1);
6544521466SIngo Weinhold	}
6644521466SIngo Weinhold
6744521466SIngo Weinhold	// exec child process
6844521466SIngo Weinhold	if (child == 0) {
6944521466SIngo Weinhold		execvp(argv[0], (char**)argv);
7044521466SIngo Weinhold		exit(1);
7144521466SIngo Weinhold	}
7244521466SIngo Weinhold
7344521466SIngo Weinhold	// wait for child
7444521466SIngo Weinhold	int childStatus;
7544521466SIngo Weinhold	while (wait(&childStatus) < 0);
7644521466SIngo Weinhold
7744521466SIngo Weinhold	return child;
7844521466SIngo Weinhold}
7944521466SIngo Weinhold
8044521466SIngo Weinhold
8144521466SIngo Weinholdvoid
8244521466SIngo Weinholddo_timing_analysis(int argc, const char* const* argv, bool schedulingAnalysis,
8344521466SIngo Weinhold	int outFD, size_t bufferSize)
8444521466SIngo Weinhold{
8544521466SIngo Weinhold	// gather initial usage info
8644521466SIngo Weinhold	thread_info initialUsage[MAX_THREADS];
8744521466SIngo Weinhold	int32 initialUsageCount = get_usage_infos(initialUsage);
8844521466SIngo Weinhold
8944521466SIngo Weinhold	// exec child process
9044521466SIngo Weinhold	bigtime_t startTime = system_time();
9144521466SIngo Weinhold	pid_t child = run_child(argc, argv);
9244521466SIngo Weinhold
9344521466SIngo Weinhold	// get child usage info
9444521466SIngo Weinhold	bigtime_t endTime = system_time();
9544521466SIngo Weinhold	bigtime_t runTime = endTime - startTime;
9644521466SIngo Weinhold	team_usage_info childUsage;
9744521466SIngo Weinhold	get_team_usage_info(B_CURRENT_TEAM, B_TEAM_USAGE_CHILDREN, &childUsage);
9844521466SIngo Weinhold
9944521466SIngo Weinhold	// gather final usage info
10044521466SIngo Weinhold	thread_info finalUsage[MAX_THREADS];
10144521466SIngo Weinhold	int32 finalUsageCount = get_usage_infos(finalUsage);
10244521466SIngo Weinhold
10344521466SIngo Weinhold	// sort the infos, so we can compare them better
10444521466SIngo Weinhold	std::sort(initialUsage, initialUsage + initialUsageCount,
10544521466SIngo Weinhold		UsageInfoThreadComparator());
10644521466SIngo Weinhold	std::sort(finalUsage, finalUsage + finalUsageCount,
10744521466SIngo Weinhold		UsageInfoThreadComparator());
10844521466SIngo Weinhold
10944521466SIngo Weinhold	// compute results
11044521466SIngo Weinhold
11144521466SIngo Weinhold	thread_info sortedThreads[MAX_THREADS];
11244521466SIngo Weinhold	int32 sortedThreadCount = 0;
11344521466SIngo Weinhold	thread_info goneThreads[MAX_THREADS];
11444521466SIngo Weinhold	int32 goneThreadCount = 0;
11544521466SIngo Weinhold
11644521466SIngo Weinhold	// child
11744521466SIngo Weinhold	sortedThreads[0].thread = child;
11844521466SIngo Weinhold	sortedThreads[0].user_time = childUsage.user_time;
11944521466SIngo Weinhold	sortedThreads[0].kernel_time = childUsage.kernel_time;
12044521466SIngo Weinhold	strlcpy(sortedThreads[0].name, "<child>", sizeof(sortedThreads[0].name));
12144521466SIngo Weinhold	sortedThreadCount++;
12244521466SIngo Weinhold
12344521466SIngo Weinhold	// other threads
12444521466SIngo Weinhold	int32 initialI = 0;
12544521466SIngo Weinhold	int32 finalI = 0;
12644521466SIngo Weinhold	while (initialI < initialUsageCount || finalI < finalUsageCount) {
12744521466SIngo Weinhold		if (initialI >= initialUsageCount
12844521466SIngo Weinhold			|| (finalI < finalUsageCount
12944521466SIngo Weinhold				&& initialUsage[initialI].thread > finalUsage[finalI].thread)) {
13044521466SIngo Weinhold			// new thread
13144521466SIngo Weinhold			memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
13244521466SIngo Weinhold				sizeof(thread_info));
13344521466SIngo Weinhold			sortedThreadCount++;
13444521466SIngo Weinhold			finalI++;
13544521466SIngo Weinhold			continue;
13644521466SIngo Weinhold		}
13744521466SIngo Weinhold
13844521466SIngo Weinhold		if (finalI >= finalUsageCount
13944521466SIngo Weinhold			|| (initialI < initialUsageCount
14044521466SIngo Weinhold				&& initialUsage[initialI].thread < finalUsage[finalI].thread)) {
14144521466SIngo Weinhold			// gone thread
14244521466SIngo Weinhold			memcpy(&goneThreads[goneThreadCount], &initialUsage[initialI],
14344521466SIngo Weinhold				sizeof(thread_info));
14444521466SIngo Weinhold			goneThreadCount++;
14544521466SIngo Weinhold			initialI++;
14644521466SIngo Weinhold			continue;
14744521466SIngo Weinhold		}
14844521466SIngo Weinhold
14944521466SIngo Weinhold		// thread is still there
15044521466SIngo Weinhold		memcpy(&sortedThreads[sortedThreadCount], &finalUsage[finalI],
15144521466SIngo Weinhold			sizeof(thread_info));
15244521466SIngo Weinhold		sortedThreads[sortedThreadCount].user_time
15344521466SIngo Weinhold			-= initialUsage[initialI].user_time;
15444521466SIngo Weinhold		sortedThreads[sortedThreadCount].kernel_time
15544521466SIngo Weinhold			-= initialUsage[initialI].kernel_time;
15644521466SIngo Weinhold		sortedThreadCount++;
15744521466SIngo Weinhold		initialI++;
15844521466SIngo Weinhold		finalI++;
15944521466SIngo Weinhold	}
16044521466SIngo Weinhold
16144521466SIngo Weinhold	// sort results
16244521466SIngo Weinhold	std::sort(sortedThreads, sortedThreads + sortedThreadCount,
16344521466SIngo Weinhold		UsageInfoTimeComparator());
16444521466SIngo Weinhold
16544521466SIngo Weinhold	// redirect output, if requested
16644521466SIngo Weinhold	if (outFD >= 0)
16744521466SIngo Weinhold		dup2(outFD, STDOUT_FILENO);
16844521466SIngo Weinhold
16944521466SIngo Weinhold	// print results
17044521466SIngo Weinhold	printf("\nTotal run time: %lld us\n", runTime);
17144521466SIngo Weinhold	printf("Thread time statistics in us:\n\n");
17244521466SIngo Weinhold	printf(" thread  name                                  kernel        user  "
17344521466SIngo Weinhold		"     total    in %%\n");
17444521466SIngo Weinhold	printf("-------------------------------------------------------------------"
17544521466SIngo Weinhold		"------------------\n");
17644521466SIngo Weinhold
17744521466SIngo Weinhold	for (int32 i = 0; i < sortedThreadCount; i++) {
17844521466SIngo Weinhold		const thread_info& info = sortedThreads[i];
17944521466SIngo Weinhold		if (info.user_time + info.kernel_time == 0)
18044521466SIngo Weinhold			break;
18144521466SIngo Weinhold
18244521466SIngo Weinhold		bool highlight = info.thread == child && isatty(STDOUT_FILENO);
18344521466SIngo Weinhold
18444521466SIngo Weinhold		if (highlight)
18544521466SIngo Weinhold			printf("\033[1m");
18644521466SIngo Weinhold
18744521466SIngo Weinhold		bigtime_t total = info.user_time + info.kernel_time;
18844521466SIngo Weinhold		printf("%7ld  %-32s  %10lld  %10lld  %10lld  %6.2f\n", info.thread,
18944521466SIngo Weinhold			info.name, info.kernel_time, info.user_time, total,
19044521466SIngo Weinhold			(double)total / runTime * 100);
19144521466SIngo Weinhold
19244521466SIngo Weinhold		if (highlight)
19344521466SIngo Weinhold			printf("\033[m");
19444521466SIngo Weinhold	}
19544521466SIngo Weinhold
19644521466SIngo Weinhold	for (int32 i = 0; i < goneThreadCount; i++) {
19744521466SIngo Weinhold		const thread_info& info = goneThreads[i];
19844521466SIngo Weinhold		printf("%7ld  %-32s  <gone>\n", info.thread, info.name);
19944521466SIngo Weinhold	}
20044521466SIngo Weinhold
20144521466SIngo Weinhold	if (schedulingAnalysis)
20244521466SIngo Weinhold		do_scheduling_analysis(startTime, endTime, bufferSize);
20344521466SIngo Weinhold}
204