<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kernel sources &#187; perf</title>
	<atom:link href="http://lynyrd.ru/category/perf/feed" rel="self" type="application/rss+xml" />
	<link>http://lynyrd.ru</link>
	<description></description>
	<lastBuildDate>Sun, 31 Jan 2010 05:51:15 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>debug.h</title>
		<link>http://lynyrd.ru/debug-h</link>
		<comments>http://lynyrd.ru/debug-h#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:57:31 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=641</guid>
		<description><![CDATA[/* For debugging general purposes */
extern int verbose;
extern int dump_trace;
int eprintf(const char *fmt, &#8230;) __attribute__((format(printf, 1, 2)));
int dump_printf(const char *fmt, &#8230;) __attribute__((format(printf, 1, 2)));
void trace_event(event_t *event);
]]></description>
			<content:encoded><![CDATA[<p>/* For debugging general purposes */<span id="more-641"></span></p>
<p>extern int verbose;<br />
extern int dump_trace;</p>
<p>int eprintf(const char *fmt, &#8230;) __attribute__((format(printf, 1, 2)));<br />
int dump_printf(const char *fmt, &#8230;) __attribute__((format(printf, 1, 2)));<br />
void trace_event(event_t *event);</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/debug-h/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>debug.c</title>
		<link>http://lynyrd.ru/debug-c</link>
		<comments>http://lynyrd.ru/debug-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:57:10 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=639</guid>
		<description><![CDATA[/* For general debugging purposes */
#include &#171;../perf.h&#187;
#include 
#include 
#include 
#include &#171;color.h&#187;
#include &#171;event.h&#187;
#include &#171;debug.h&#187;
int verbose = 0;
int dump_trace = 0;
int eprintf(const char *fmt, &#8230;)
{
	va_list args;
	int ret = 0;
	if (verbose) {
		va_start(args, fmt);
		ret = vfprintf(stderr, fmt, args);
		va_end(args);
	}
	return ret;
}
int dump_printf(const char *fmt, &#8230;)
{
	va_list args;
	int ret = 0;
	if (dump_trace) {
		va_start(args, fmt);
		ret = vprintf(fmt, args);
		va_end(args);
	}
	return ret;
}
static int dump_printf_color(const char *fmt, const ]]></description>
			<content:encoded><![CDATA[<p>/* For general debugging purposes */<span id="more-639"></span></p>
<p>#include &laquo;../perf.h&raquo;</p>
<p>#include <string.h><br />
#include <stdarg.h><br />
#include <stdio.h></p>
<p>#include &laquo;color.h&raquo;<br />
#include &laquo;event.h&raquo;<br />
#include &laquo;debug.h&raquo;</p>
<p>int verbose = 0;<br />
int dump_trace = 0;</p>
<p>int eprintf(const char *fmt, &#8230;)<br />
{<br />
	va_list args;<br />
	int ret = 0;</p>
<p>	if (verbose) {<br />
		va_start(args, fmt);<br />
		ret = vfprintf(stderr, fmt, args);<br />
		va_end(args);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>int dump_printf(const char *fmt, &#8230;)<br />
{<br />
	va_list args;<br />
	int ret = 0;</p>
<p>	if (dump_trace) {<br />
		va_start(args, fmt);<br />
		ret = vprintf(fmt, args);<br />
		va_end(args);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static int dump_printf_color(const char *fmt, const char *color, &#8230;)<br />
{<br />
	va_list args;<br />
	int ret = 0;</p>
<p>	if (dump_trace) {<br />
		va_start(args, color);<br />
		ret = color_vfprintf(stdout, color, fmt, args);<br />
		va_end(args);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>void trace_event(event_t *event)<br />
{<br />
	unsigned char *raw_event = (void *)event;<br />
	const char *color = PERF_COLOR_BLUE;<br />
	int i, j;</p>
<p>	if (!dump_trace)<br />
		return;</p>
<p>	dump_printf(&raquo;.&raquo;);<br />
	dump_printf_color(&raquo;\n. &#8230; raw event: size %d bytes\n&raquo;, color,<br />
			  event->header.size);</p>
<p>	for (i = 0; i < event->header.size; i++) {<br />
		if ((i &#038; 15) == 0) {<br />
			dump_printf(&raquo;.&raquo;);<br />
			dump_printf_color(&raquo;  %04x: &laquo;, color, i);<br />
		}</p>
<p>		dump_printf_color(&raquo; %02x&raquo;, color, raw_event[i]);</p>
<p>		if (((i &#038; 15) == 15) || i == event->header.size-1) {<br />
			dump_printf_color(&raquo;  &laquo;, color);<br />
			for (j = 0; j < 15-(i &#038; 15); j++)<br />
				dump_printf_color(&raquo;   &laquo;, color);<br />
			for (j = 0; j < (i &#038; 15); j++) {<br />
				if (isprint(raw_event[i-15+j]))<br />
					dump_printf_color(&raquo;%c&raquo;, color,<br />
							  raw_event[i-15+j]);<br />
				else<br />
					dump_printf_color(&raquo;.&raquo;, color);<br />
			}<br />
			dump_printf_color(&raquo;\n&raquo;, color);<br />
		}<br />
	}<br />
	dump_printf(&raquo;.\n&raquo;);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/debug-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ctype.c</title>
		<link>http://lynyrd.ru/ctype-c</link>
		<comments>http://lynyrd.ru/ctype-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:56:33 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=637</guid>
		<description><![CDATA[/*
 * Sane locale-independent, ASCII ctype.
 *
 * No surprises, and works with signed and unsigned chars.
 */
#include &#171;cache.h&#187;
enum {
	S = GIT_SPACE,
	A = GIT_ALPHA,
	D = GIT_DIGIT,
	G = GIT_GLOB_SPECIAL,	/* *, ?, [, \\ */
	R = GIT_REGEX_SPECIAL,	/* $, (, ), +, ., ^, {, &#124; * */
	P = GIT_PRINT_EXTRA,	/* printable - alpha - digit - glob - ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * Sane locale-independent, ASCII ctype.<span id="more-637"></span><br />
 *<br />
 * No surprises, and works with signed and unsigned chars.<br />
 */<br />
#include &laquo;cache.h&raquo;</p>
<p>enum {<br />
	S = GIT_SPACE,<br />
	A = GIT_ALPHA,<br />
	D = GIT_DIGIT,<br />
	G = GIT_GLOB_SPECIAL,	/* *, ?, [, \\ */<br />
	R = GIT_REGEX_SPECIAL,	/* $, (, ), +, ., ^, {, | * */<br />
	P = GIT_PRINT_EXTRA,	/* printable - alpha - digit - glob - regex */</p>
<p>	PS = GIT_SPACE | GIT_PRINT_EXTRA,<br />
};</p>
<p>unsigned char sane_ctype[256] = {<br />
/*	0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F			    */</p>
<p>	0, 0, 0, 0, 0, 0, 0, 0, 0, S, S, 0, 0, S, 0, 0,		/*   0.. 15 */<br />
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,		/*  16.. 31 */<br />
	PS,P, P, P, R, P, P, P, R, R, G, R, P, P, R, P,		/*  32.. 47 */<br />
	D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G,		/*  48.. 63 */<br />
	P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  64.. 79 */<br />
	A, A, A, A, A, A, A, A, A, A, A, G, G, P, R, P,		/*  80.. 95 */<br />
	P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A,		/*  96..111 */<br />
	A, A, A, A, A, A, A, A, A, A, A, R, R, P, P, 0,		/* 112..127 */<br />
	/* Nothing in the 128.. range */<br />
};</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/ctype-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>config.c</title>
		<link>http://lynyrd.ru/config-c</link>
		<comments>http://lynyrd.ru/config-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:55:40 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=635</guid>
		<description><![CDATA[/*
 * GIT &#8211; The information manager from hell
 *
 * Copyright (C) Linus Torvalds, 2005
 * Copyright (C) Johannes Schindelin, 2005
 *
 */
#include &#171;util.h&#187;
#include &#171;cache.h&#187;
#include &#171;exec_cmd.h&#187;
#define MAXNAME (256)
static FILE *config_file;
static const char *config_file_name;
static int config_linenr;
static int config_file_eof;
const char *config_exclusive_filename = NULL;
static int get_next_char(void)
{
	int c;
	FILE *f;
	c = &#8216;\n&#8217;;
	if ((f = config_file) != NULL) {
		c = ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * GIT &#8211; The information manager from hell<span id="more-635"></span><br />
 *<br />
 * Copyright (C) Linus Torvalds, 2005<br />
 * Copyright (C) Johannes Schindelin, 2005<br />
 *<br />
 */<br />
#include &laquo;util.h&raquo;<br />
#include &laquo;cache.h&raquo;<br />
#include &laquo;exec_cmd.h&raquo;</p>
<p>#define MAXNAME (256)</p>
<p>static FILE *config_file;<br />
static const char *config_file_name;<br />
static int config_linenr;<br />
static int config_file_eof;</p>
<p>const char *config_exclusive_filename = NULL;</p>
<p>static int get_next_char(void)<br />
{<br />
	int c;<br />
	FILE *f;</p>
<p>	c = &#8216;\n&#8217;;<br />
	if ((f = config_file) != NULL) {<br />
		c = fgetc(f);<br />
		if (c == &#8216;\r&#8217;) {<br />
			/* DOS like systems */<br />
			c = fgetc(f);<br />
			if (c != &#8216;\n&#8217;) {<br />
				ungetc(c, f);<br />
				c = &#8216;\r&#8217;;<br />
			}<br />
		}<br />
		if (c == &#8216;\n&#8217;)<br />
			config_linenr++;<br />
		if (c == EOF) {<br />
			config_file_eof = 1;<br />
			c = &#8216;\n&#8217;;<br />
		}<br />
	}<br />
	return c;<br />
}</p>
<p>static char *parse_value(void)<br />
{<br />
	static char value[1024];<br />
	int quote = 0, comment = 0, space = 0;<br />
	size_t len = 0;</p>
<p>	for (;;) {<br />
		int c = get_next_char();</p>
<p>		if (len >= sizeof(value) &#8211; 1)<br />
			return NULL;<br />
		if (c == &#8216;\n&#8217;) {<br />
			if (quote)<br />
				return NULL;<br />
			value[len] = 0;<br />
			return value;<br />
		}<br />
		if (comment)<br />
			continue;<br />
		if (isspace(c) &#038;&#038; !quote) {<br />
			space = 1;<br />
			continue;<br />
		}<br />
		if (!quote) {<br />
			if (c == &#8216;;&#8217; || c == &#8216;#&#8217;) {<br />
				comment = 1;<br />
				continue;<br />
			}<br />
		}<br />
		if (space) {<br />
			if (len)<br />
				value[len++] = &#8216; &#8216;;<br />
			space = 0;<br />
		}<br />
		if (c == &#8216;\\&#8217;) {<br />
			c = get_next_char();<br />
			switch (c) {<br />
			case &#8216;\n&#8217;:<br />
				continue;<br />
			case &#8216;t&#8217;:<br />
				c = &#8216;\t&#8217;;<br />
				break;<br />
			case &#8216;b&#8217;:<br />
				c = &#8216;\b&#8217;;<br />
				break;<br />
			case &#8216;n&#8217;:<br />
				c = &#8216;\n&#8217;;<br />
				break;<br />
			/* Some characters escape as themselves */<br />
			case &#8216;\\&#8217;: case &#8216;&raquo;&#8216;:<br />
				break;<br />
			/* Reject unknown escape sequences */<br />
			default:<br />
				return NULL;<br />
			}<br />
			value[len++] = c;<br />
			continue;<br />
		}<br />
		if (c == &#8216;&raquo;&#8216;) {<br />
			quote = 1-quote;<br />
			continue;<br />
		}<br />
		value[len++] = c;<br />
	}<br />
}</p>
<p>static inline int iskeychar(int c)<br />
{<br />
	return isalnum(c) || c == &#8216;-&#8217;;<br />
}</p>
<p>static int get_value(config_fn_t fn, void *data, char *name, unsigned int len)<br />
{<br />
	int c;<br />
	char *value;</p>
<p>	/* Get the full name */<br />
	for (;;) {<br />
		c = get_next_char();<br />
		if (config_file_eof)<br />
			break;<br />
		if (!iskeychar(c))<br />
			break;<br />
		name[len++] = tolower(c);<br />
		if (len >= MAXNAME)<br />
			return -1;<br />
	}<br />
	name[len] = 0;<br />
	while (c == &#8216; &#8216; || c == &#8216;\t&#8217;)<br />
		c = get_next_char();</p>
<p>	value = NULL;<br />
	if (c != &#8216;\n&#8217;) {<br />
		if (c != &#8216;=&#8217;)<br />
			return -1;<br />
		value = parse_value();<br />
		if (!value)<br />
			return -1;<br />
	}<br />
	return fn(name, value, data);<br />
}</p>
<p>static int get_extended_base_var(char *name, int baselen, int c)<br />
{<br />
	do {<br />
		if (c == &#8216;\n&#8217;)<br />
			return -1;<br />
		c = get_next_char();<br />
	} while (isspace(c));</p>
<p>	/* We require the format to be &#8216;[base "extension"]&#8216; */<br />
	if (c != &#8216;&raquo;&#8216;)<br />
		return -1;<br />
	name[baselen++] = &#8216;.&#8217;;</p>
<p>	for (;;) {<br />
		int ch = get_next_char();</p>
<p>		if (ch == &#8216;\n&#8217;)<br />
			return -1;<br />
		if (ch == &#8216;&raquo;&#8216;)<br />
			break;<br />
		if (ch == &#8216;\\&#8217;) {<br />
			ch = get_next_char();<br />
			if (ch == &#8216;\n&#8217;)<br />
				return -1;<br />
		}<br />
		name[baselen++] = ch;<br />
		if (baselen > MAXNAME / 2)<br />
			return -1;<br />
	}</p>
<p>	/* Final &#8216;]&#8217; */<br />
	if (get_next_char() != &#8216;]&#8217;)<br />
		return -1;<br />
	return baselen;<br />
}</p>
<p>static int get_base_var(char *name)<br />
{<br />
	int baselen = 0;</p>
<p>	for (;;) {<br />
		int c = get_next_char();<br />
		if (config_file_eof)<br />
			return -1;<br />
		if (c == &#8216;]&#8217;)<br />
			return baselen;<br />
		if (isspace(c))<br />
			return get_extended_base_var(name, baselen, c);<br />
		if (!iskeychar(c) &#038;&#038; c != &#8216;.&#8217;)<br />
			return -1;<br />
		if (baselen > MAXNAME / 2)<br />
			return -1;<br />
		name[baselen++] = tolower(c);<br />
	}<br />
}</p>
<p>static int perf_parse_file(config_fn_t fn, void *data)<br />
{<br />
	int comment = 0;<br />
	int baselen = 0;<br />
	static char var[MAXNAME];</p>
<p>	/* U+FEFF Byte Order Mark in UTF8 */<br />
	static const unsigned char *utf8_bom = (unsigned char *) &laquo;\xef\xbb\xbf&raquo;;<br />
	const unsigned char *bomptr = utf8_bom;</p>
<p>	for (;;) {<br />
		int c = get_next_char();<br />
		if (bomptr &#038;&#038; *bomptr) {<br />
			/* We are at the file beginning; skip UTF8-encoded BOM<br />
			 * if present. Sane editors won&#8217;t put this in on their<br />
			 * own, but e.g. Windows Notepad will do it happily. */<br />
			if ((unsigned char) c == *bomptr) {<br />
				bomptr++;<br />
				continue;<br />
			} else {<br />
				/* Do not tolerate partial BOM. */<br />
				if (bomptr != utf8_bom)<br />
					break;<br />
				/* No BOM at file beginning. Cool. */<br />
				bomptr = NULL;<br />
			}<br />
		}<br />
		if (c == &#8216;\n&#8217;) {<br />
			if (config_file_eof)<br />
				return 0;<br />
			comment = 0;<br />
			continue;<br />
		}<br />
		if (comment || isspace(c))<br />
			continue;<br />
		if (c == &#8216;#&#8217; || c == &#8216;;&#8217;) {<br />
			comment = 1;<br />
			continue;<br />
		}<br />
		if (c == &#8216;[') {<br />
			baselen = get_base_var(var);<br />
			if (baselen <= 0)<br />
				break;<br />
			var[baselen++] = &#8216;.&#8217;;<br />
			var[baselen] = 0;<br />
			continue;<br />
		}<br />
		if (!isalpha(c))<br />
			break;<br />
		var[baselen] = tolower(c);<br />
		if (get_value(fn, data, var, baselen+1) < 0)<br />
			break;<br />
	}<br />
	die("bad config file line %d in %s", config_linenr, config_file_name);<br />
}</p>
<p>static int parse_unit_factor(const char *end, unsigned long *val)<br />
{<br />
	if (!*end)<br />
		return 1;<br />
	else if (!strcasecmp(end, "k")) {<br />
		*val *= 1024;<br />
		return 1;<br />
	}<br />
	else if (!strcasecmp(end, "m")) {<br />
		*val *= 1024 * 1024;<br />
		return 1;<br />
	}<br />
	else if (!strcasecmp(end, "g")) {<br />
		*val *= 1024 * 1024 * 1024;<br />
		return 1;<br />
	}<br />
	return 0;<br />
}</p>
<p>static int perf_parse_long(const char *value, long *ret)<br />
{<br />
	if (value &#038;&#038; *value) {<br />
		char *end;<br />
		long val = strtol(value, &#038;end, 0);<br />
		unsigned long factor = 1;<br />
		if (!parse_unit_factor(end, &#038;factor))<br />
			return 0;<br />
		*ret = val * factor;<br />
		return 1;<br />
	}<br />
	return 0;<br />
}</p>
<p>int perf_parse_ulong(const char *value, unsigned long *ret)<br />
{<br />
	if (value &#038;&#038; *value) {<br />
		char *end;<br />
		unsigned long val = strtoul(value, &#038;end, 0);<br />
		if (!parse_unit_factor(end, &#038;val))<br />
			return 0;<br />
		*ret = val;<br />
		return 1;<br />
	}<br />
	return 0;<br />
}</p>
<p>static void die_bad_config(const char *name)<br />
{<br />
	if (config_file_name)<br />
		die("bad config value for '%s' in %s", name, config_file_name);<br />
	die("bad config value for '%s'", name);<br />
}</p>
<p>int perf_config_int(const char *name, const char *value)<br />
{<br />
	long ret = 0;<br />
	if (!perf_parse_long(value, &#038;ret))<br />
		die_bad_config(name);<br />
	return ret;<br />
}</p>
<p>unsigned long perf_config_ulong(const char *name, const char *value)<br />
{<br />
	unsigned long ret;<br />
	if (!perf_parse_ulong(value, &#038;ret))<br />
		die_bad_config(name);<br />
	return ret;<br />
}</p>
<p>int perf_config_bool_or_int(const char *name, const char *value, int *is_bool)<br />
{<br />
	*is_bool = 1;<br />
	if (!value)<br />
		return 1;<br />
	if (!*value)<br />
		return 0;<br />
	if (!strcasecmp(value, "true") || !strcasecmp(value, "yes") || !strcasecmp(value, "on"))<br />
		return 1;<br />
	if (!strcasecmp(value, "false") || !strcasecmp(value, "no") || !strcasecmp(value, "off"))<br />
		return 0;<br />
	*is_bool = 0;<br />
	return perf_config_int(name, value);<br />
}</p>
<p>int perf_config_bool(const char *name, const char *value)<br />
{<br />
	int discard;<br />
	return !!perf_config_bool_or_int(name, value, &#038;discard);<br />
}</p>
<p>int perf_config_string(const char **dest, const char *var, const char *value)<br />
{<br />
	if (!value)<br />
		return config_error_nonbool(var);<br />
	*dest = strdup(value);<br />
	return 0;<br />
}</p>
<p>static int perf_default_core_config(const char *var __used, const char *value __used)<br />
{<br />
	/* Add other config variables here and to Documentation/config.txt. */<br />
	return 0;<br />
}</p>
<p>int perf_default_config(const char *var, const char *value, void *dummy __used)<br />
{<br />
	if (!prefixcmp(var, "core."))<br />
		return perf_default_core_config(var, value);</p>
<p>	/* Add other config variables here and to Documentation/config.txt. */<br />
	return 0;<br />
}</p>
<p>int perf_config_from_file(config_fn_t fn, const char *filename, void *data)<br />
{<br />
	int ret;<br />
	FILE *f = fopen(filename, "r");</p>
<p>	ret = -1;<br />
	if (f) {<br />
		config_file = f;<br />
		config_file_name = filename;<br />
		config_linenr = 1;<br />
		config_file_eof = 0;<br />
		ret = perf_parse_file(fn, data);<br />
		fclose(f);<br />
		config_file_name = NULL;<br />
	}<br />
	return ret;<br />
}</p>
<p>const char *perf_etc_perfconfig(void)<br />
{<br />
	static const char *system_wide;<br />
	if (!system_wide)<br />
		system_wide = system_path(ETC_PERFCONFIG);<br />
	return system_wide;<br />
}</p>
<p>static int perf_env_bool(const char *k, int def)<br />
{<br />
	const char *v = getenv(k);<br />
	return v ? perf_config_bool(k, v) : def;<br />
}</p>
<p>int perf_config_system(void)<br />
{<br />
	return !perf_env_bool("PERF_CONFIG_NOSYSTEM", 0);<br />
}</p>
<p>int perf_config_global(void)<br />
{<br />
	return !perf_env_bool("PERF_CONFIG_NOGLOBAL", 0);<br />
}</p>
<p>int perf_config(config_fn_t fn, void *data)<br />
{<br />
	int ret = 0, found = 0;<br />
	char *repo_config = NULL;<br />
	const char *home = NULL;</p>
<p>	/* Setting $PERF_CONFIG makes perf read _only_ the given config file. */<br />
	if (config_exclusive_filename)<br />
		return perf_config_from_file(fn, config_exclusive_filename, data);<br />
	if (perf_config_system() &#038;&#038; !access(perf_etc_perfconfig(), R_OK)) {<br />
		ret += perf_config_from_file(fn, perf_etc_perfconfig(),<br />
					    data);<br />
		found += 1;<br />
	}</p>
<p>	home = getenv("HOME");<br />
	if (perf_config_global() &#038;&#038; home) {<br />
		char *user_config = strdup(mkpath("%s/.perfconfig", home));<br />
		if (!access(user_config, R_OK)) {<br />
			ret += perf_config_from_file(fn, user_config, data);<br />
			found += 1;<br />
		}<br />
		free(user_config);<br />
	}</p>
<p>	repo_config = perf_pathdup("config");<br />
	if (!access(repo_config, R_OK)) {<br />
		ret += perf_config_from_file(fn, repo_config, data);<br />
		found += 1;<br />
	}<br />
	free(repo_config);<br />
	if (found == 0)<br />
		return -1;<br />
	return ret;<br />
}</p>
<p>/*<br />
 * Find all the stuff for perf_config_set() below.<br />
 */</p>
<p>#define MAX_MATCHES 512</p>
<p>static struct {<br />
	int baselen;<br />
	char* key;<br />
	int do_not_match;<br />
	regex_t* value_regex;<br />
	int multi_replace;<br />
	size_t offset[MAX_MATCHES];<br />
	enum { START, SECTION_SEEN, SECTION_END_SEEN, KEY_SEEN } state;<br />
	int seen;<br />
} store;</p>
<p>static int matches(const char* key, const char* value)<br />
{<br />
	return !strcmp(key, store.key) &#038;&#038;<br />
		(store.value_regex == NULL ||<br />
		 (store.do_not_match ^<br />
		  !regexec(store.value_regex, value, 0, NULL, 0)));<br />
}</p>
<p>static int store_aux(const char* key, const char* value, void *cb __used)<br />
{<br />
	int section_len;<br />
	const char *ep;</p>
<p>	switch (store.state) {<br />
	case KEY_SEEN:<br />
		if (matches(key, value)) {<br />
			if (store.seen == 1 &#038;&#038; store.multi_replace == 0) {<br />
				warning("%s has multiple values", key);<br />
			} else if (store.seen >= MAX_MATCHES) {<br />
				error(&raquo;too many matches for %s&raquo;, key);<br />
				return 1;<br />
			}</p>
<p>			store.offset[store.seen] = ftell(config_file);<br />
			store.seen++;<br />
		}<br />
		break;<br />
	case SECTION_SEEN:<br />
		/*<br />
		 * What we are looking for is in store.key (both<br />
		 * section and var), and its section part is baselen<br />
		 * long.  We found key (again, both section and var).<br />
		 * We would want to know if this key is in the same<br />
		 * section as what we are looking for.  We already<br />
		 * know we are in the same section as what should<br />
		 * hold store.key.<br />
		 */<br />
		ep = strrchr(key, &#8216;.&#8217;);<br />
		section_len = ep &#8211; key;</p>
<p>		if ((section_len != store.baselen) ||<br />
		    memcmp(key, store.key, section_len+1)) {<br />
			store.state = SECTION_END_SEEN;<br />
			break;<br />
		}</p>
<p>		/*<br />
		 * Do not increment matches: this is no match, but we<br />
		 * just made sure we are in the desired section.<br />
		 */<br />
		store.offset[store.seen] = ftell(config_file);<br />
		/* fallthru */<br />
	case SECTION_END_SEEN:<br />
	case START:<br />
		if (matches(key, value)) {<br />
			store.offset[store.seen] = ftell(config_file);<br />
			store.state = KEY_SEEN;<br />
			store.seen++;<br />
		} else {<br />
			if (strrchr(key, &#8216;.&#8217;) &#8211; key == store.baselen &#038;&#038;<br />
			      !strncmp(key, store.key, store.baselen)) {<br />
					store.state = SECTION_SEEN;<br />
					store.offset[store.seen] = ftell(config_file);<br />
			}<br />
		}<br />
	default:<br />
		break;<br />
	}<br />
	return 0;<br />
}</p>
<p>static int store_write_section(int fd, const char* key)<br />
{<br />
	const char *dot;<br />
	int i, success;<br />
	struct strbuf sb = STRBUF_INIT;</p>
<p>	dot = memchr(key, &#8216;.&#8217;, store.baselen);<br />
	if (dot) {<br />
		strbuf_addf(&#038;sb, &laquo;[%.*s \"", (int)(dot - key), key);<br />
		for (i = dot - key + 1; i < store.baselen; i++) {<br />
			if (key[i] == &#8216;&raquo;&#8216; || key[i] == &#8216;\\&#8217;)<br />
				strbuf_addch(&#038;sb, &#8216;\\&#8217;);<br />
			strbuf_addch(&#038;sb, key[i]);<br />
		}<br />
		strbuf_addstr(&#038;sb, &laquo;\&raquo;]\n&raquo;);<br />
	} else {<br />
		strbuf_addf(&#038;sb, &laquo;[%.*s]\n&raquo;, store.baselen, key);<br />
	}</p>
<p>	success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);<br />
	strbuf_release(&#038;sb);</p>
<p>	return success;<br />
}</p>
<p>static int store_write_pair(int fd, const char* key, const char* value)<br />
{<br />
	int i, success;<br />
	int length = strlen(key + store.baselen + 1);<br />
	const char *quote = &laquo;&raquo;;<br />
	struct strbuf sb = STRBUF_INIT;</p>
<p>	/*<br />
	 * Check to see if the value needs to be surrounded with a dq pair.<br />
	 * Note that problematic characters are always backslash-quoted; this<br />
	 * check is about not losing leading or trailing SP and strings that<br />
	 * follow beginning-of-comment characters (i.e. &#8216;;&#8217; and &#8216;#&#8217;) by the<br />
	 * configuration parser.<br />
	 */<br />
	if (value[0] == &#8216; &#8216;)<br />
		quote = &laquo;\&raquo;";<br />
	for (i = 0; value[i]; i++)<br />
		if (value[i] == &#8216;;&#8217; || value[i] == &#8216;#&#8217;)<br />
			quote = &laquo;\&raquo;";<br />
	if (i &#038;&#038; value[i - 1] == &#8216; &#8216;)<br />
		quote = &laquo;\&raquo;";</p>
<p>	strbuf_addf(&#038;sb, &laquo;\t%.*s = %s&raquo;,<br />
		    length, key + store.baselen + 1, quote);</p>
<p>	for (i = 0; value[i]; i++)<br />
		switch (value[i]) {<br />
		case &#8216;\n&#8217;:<br />
			strbuf_addstr(&#038;sb, &laquo;\\n&raquo;);<br />
			break;<br />
		case &#8216;\t&#8217;:<br />
			strbuf_addstr(&#038;sb, &laquo;\\t&raquo;);<br />
			break;<br />
		case &#8216;&raquo;&#8216;:<br />
		case &#8216;\\&#8217;:<br />
			strbuf_addch(&#038;sb, &#8216;\\&#8217;);<br />
		default:<br />
			strbuf_addch(&#038;sb, value[i]);<br />
			break;<br />
		}<br />
	strbuf_addf(&#038;sb, &laquo;%s\n&raquo;, quote);</p>
<p>	success = (write_in_full(fd, sb.buf, sb.len) == (ssize_t)sb.len);<br />
	strbuf_release(&#038;sb);</p>
<p>	return success;<br />
}</p>
<p>static ssize_t find_beginning_of_line(const char* contents, size_t size,<br />
	size_t offset_, int* found_bracket)<br />
{<br />
	size_t equal_offset = size, bracket_offset = size;<br />
	ssize_t offset;</p>
<p>contline:<br />
	for (offset = offset_-2; offset > 0<br />
			&#038;&#038; contents[offset] != &#8216;\n&#8217;; offset&#8211;)<br />
		switch (contents[offset]) {<br />
			case &#8216;=&#8217;: equal_offset = offset; break;<br />
			case &#8216;]&#8217;: bracket_offset = offset; break;<br />
			default: break;<br />
		}<br />
	if (offset > 0 &#038;&#038; contents[offset-1] == &#8216;\\&#8217;) {<br />
		offset_ = offset;<br />
		goto contline;<br />
	}<br />
	if (bracket_offset < equal_offset) {<br />
		*found_bracket = 1;<br />
		offset = bracket_offset+1;<br />
	} else<br />
		offset++;</p>
<p>	return offset;<br />
}</p>
<p>int perf_config_set(const char* key, const char* value)<br />
{<br />
	return perf_config_set_multivar(key, value, NULL, 0);<br />
}</p>
<p>/*<br />
 * If value==NULL, unset in (remove from) config,<br />
 * if value_regex!=NULL, disregard key/value pairs where value does not match.<br />
 * if multi_replace==0, nothing, or only one matching key/value is replaced,<br />
 *     else all matching key/values (regardless how many) are removed,<br />
 *     before the new pair is written.<br />
 *<br />
 * Returns 0 on success.<br />
 *<br />
 * This function does this:<br />
 *<br />
 * - it locks the config file by creating ".perf/config.lock"<br />
 *<br />
 * - it then parses the config using store_aux() as validator to find<br />
 *   the position on the key/value pair to replace. If it is to be unset,<br />
 *   it must be found exactly once.<br />
 *<br />
 * - the config file is mmap()ed and the part before the match (if any) is<br />
 *   written to the lock file, then the changed part and the rest.<br />
 *<br />
 * - the config file is removed and the lock file rename()d to it.<br />
 *<br />
 */<br />
int perf_config_set_multivar(const char* key, const char* value,<br />
	const char* value_regex, int multi_replace)<br />
{<br />
	int i, dot;<br />
	int fd = -1, in_fd;<br />
	int ret = 0;<br />
	char* config_filename;<br />
	const char* last_dot = strrchr(key, '.');</p>
<p>	if (config_exclusive_filename)<br />
		config_filename = strdup(config_exclusive_filename);<br />
	else<br />
		config_filename = perf_pathdup("config");</p>
<p>	/*<br />
	 * Since "key" actually contains the section name and the real<br />
	 * key name separated by a dot, we have to know where the dot is.<br />
	 */</p>
<p>	if (last_dot == NULL) {<br />
		error("key does not contain a section: %s", key);<br />
		ret = 2;<br />
		goto out_free;<br />
	}<br />
	store.baselen = last_dot - key;</p>
<p>	store.multi_replace = multi_replace;</p>
<p>	/*<br />
	 * Validate the key and while at it, lower case it for matching.<br />
	 */<br />
	store.key = malloc(strlen(key) + 1);<br />
	dot = 0;<br />
	for (i = 0; key[i]; i++) {<br />
		unsigned char c = key[i];<br />
		if (c == '.')<br />
			dot = 1;<br />
		/* Leave the extended basename untouched.. */<br />
		if (!dot || i > store.baselen) {<br />
			if (!iskeychar(c) || (i == store.baselen+1 &#038;&#038; !isalpha(c))) {<br />
				error(&raquo;invalid key: %s&raquo;, key);<br />
				free(store.key);<br />
				ret = 1;<br />
				goto out_free;<br />
			}<br />
			c = tolower(c);<br />
		} else if (c == &#8216;\n&#8217;) {<br />
			error(&raquo;invalid key (newline): %s&raquo;, key);<br />
			free(store.key);<br />
			ret = 1;<br />
			goto out_free;<br />
		}<br />
		store.key[i] = c;<br />
	}<br />
	store.key[i] = 0;</p>
<p>	/*<br />
	 * If .perf/config does not exist yet, write a minimal version.<br />
	 */<br />
	in_fd = open(config_filename, O_RDONLY);<br />
	if ( in_fd < 0 ) {<br />
		free(store.key);</p>
<p>		if ( ENOENT != errno ) {<br />
			error("opening %s: %s", config_filename,<br />
			      strerror(errno));<br />
			ret = 3; /* same as "invalid config file" */<br />
			goto out_free;<br />
		}<br />
		/* if nothing to unset, error out */<br />
		if (value == NULL) {<br />
			ret = 5;<br />
			goto out_free;<br />
		}</p>
<p>		store.key = (char*)key;<br />
		if (!store_write_section(fd, key) ||<br />
		    !store_write_pair(fd, key, value))<br />
			goto write_err_out;<br />
	} else {<br />
		struct stat st;<br />
		char *contents;<br />
		ssize_t contents_sz, copy_begin, copy_end;<br />
		int new_line = 0;</p>
<p>		if (value_regex == NULL)<br />
			store.value_regex = NULL;<br />
		else {<br />
			if (value_regex[0] == '!') {<br />
				store.do_not_match = 1;<br />
				value_regex++;<br />
			} else<br />
				store.do_not_match = 0;</p>
<p>			store.value_regex = (regex_t*)malloc(sizeof(regex_t));<br />
			if (regcomp(store.value_regex, value_regex,<br />
					REG_EXTENDED)) {<br />
				error("invalid pattern: %s", value_regex);<br />
				free(store.value_regex);<br />
				ret = 6;<br />
				goto out_free;<br />
			}<br />
		}</p>
<p>		store.offset[0] = 0;<br />
		store.state = START;<br />
		store.seen = 0;</p>
<p>		/*<br />
		 * After this, store.offset will contain the *end* offset<br />
		 * of the last match, or remain at 0 if no match was found.<br />
		 * As a side effect, we make sure to transform only a valid<br />
		 * existing config file.<br />
		 */<br />
		if (perf_config_from_file(store_aux, config_filename, NULL)) {<br />
			error("invalid config file %s", config_filename);<br />
			free(store.key);<br />
			if (store.value_regex != NULL) {<br />
				regfree(store.value_regex);<br />
				free(store.value_regex);<br />
			}<br />
			ret = 3;<br />
			goto out_free;<br />
		}</p>
<p>		free(store.key);<br />
		if (store.value_regex != NULL) {<br />
			regfree(store.value_regex);<br />
			free(store.value_regex);<br />
		}</p>
<p>		/* if nothing to unset, or too many matches, error out */<br />
		if ((store.seen == 0 &#038;&#038; value == NULL) ||<br />
				(store.seen > 1 &#038;&#038; multi_replace == 0)) {<br />
			ret = 5;<br />
			goto out_free;<br />
		}</p>
<p>		fstat(in_fd, &#038;st);<br />
		contents_sz = xsize_t(st.st_size);<br />
		contents = mmap(NULL, contents_sz, PROT_READ,<br />
			MAP_PRIVATE, in_fd, 0);<br />
		close(in_fd);</p>
<p>		if (store.seen == 0)<br />
			store.seen = 1;</p>
<p>		for (i = 0, copy_begin = 0; i < store.seen; i++) {<br />
			if (store.offset[i] == 0) {<br />
				store.offset[i] = copy_end = contents_sz;<br />
			} else if (store.state != KEY_SEEN) {<br />
				copy_end = store.offset[i];<br />
			} else<br />
				copy_end = find_beginning_of_line(<br />
					contents, contents_sz,<br />
					store.offset[i]-2, &#038;new_line);</p>
<p>			if (copy_end > 0 &#038;&#038; contents[copy_end-1] != &#8216;\n&#8217;)<br />
				new_line = 1;</p>
<p>			/* write the first part of the config */<br />
			if (copy_end > copy_begin) {<br />
				if (write_in_full(fd, contents + copy_begin,<br />
						  copy_end &#8211; copy_begin) <<br />
				    copy_end &#8211; copy_begin)<br />
					goto write_err_out;<br />
				if (new_line &#038;&#038;<br />
				    write_in_full(fd, &laquo;\n&raquo;, 1) != 1)<br />
					goto write_err_out;<br />
			}<br />
			copy_begin = store.offset[i];<br />
		}</p>
<p>		/* write the pair (value == NULL means unset) */<br />
		if (value != NULL) {<br />
			if (store.state == START) {<br />
				if (!store_write_section(fd, key))<br />
					goto write_err_out;<br />
			}<br />
			if (!store_write_pair(fd, key, value))<br />
				goto write_err_out;<br />
		}</p>
<p>		/* write the rest of the config */<br />
		if (copy_begin < contents_sz)<br />
			if (write_in_full(fd, contents + copy_begin,<br />
					  contents_sz &#8211; copy_begin) <<br />
			    contents_sz &#8211; copy_begin)<br />
				goto write_err_out;</p>
<p>		munmap(contents, contents_sz);<br />
	}</p>
<p>	ret = 0;</p>
<p>out_free:<br />
	free(config_filename);<br />
	return ret;</p>
<p>write_err_out:<br />
	goto out_free;</p>
<p>}</p>
<p>/*<br />
 * Call this to report error for your variable that should not<br />
 * get a boolean value (i.e. &laquo;[my] var&raquo; means &laquo;true&raquo;).<br />
 */<br />
int config_error_nonbool(const char *var)<br />
{<br />
	return error(&raquo;Missing value for &#8216;%s&#8217;&raquo;, var);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/config-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>color.h</title>
		<link>http://lynyrd.ru/color-h</link>
		<comments>http://lynyrd.ru/color-h#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:54:37 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=633</guid>
		<description><![CDATA[#ifndef COLOR_H
#define COLOR_H
/* &#171;\033[1;38;5;2xx;48;5;2xxm\0&#8243; is 23 bytes */
#define COLOR_MAXLEN 24
#define PERF_COLOR_NORMAL	&#171;&#187;
#define PERF_COLOR_RESET	&#171;\033[m&#187;
#define PERF_COLOR_BOLD		&#171;\033[1m&#187;
#define PERF_COLOR_RED		&#171;\033[31m&#187;
#define PERF_COLOR_GREEN	&#171;\033[32m&#187;
#define PERF_COLOR_YELLOW	&#171;\033[33m&#187;
#define PERF_COLOR_BLUE		&#171;\033[34m&#187;
#define PERF_COLOR_MAGENTA	&#171;\033[35m&#187;
#define PERF_COLOR_CYAN		&#171;\033[36m&#187;
#define PERF_COLOR_BG_RED	&#171;\033[41m&#187;
#define MIN_GREEN	0.5
#define MIN_RED		5.0
/*
 * This variable stores the value of color.ui
 */
extern int perf_use_color_default;
/*
 * Use this instead of perf_default_config if you need the value of color.ui.
 */
int perf_color_default_config(const char *var, const char *value, void *cb);
int ]]></description>
			<content:encoded><![CDATA[<p>#ifndef COLOR_H<br />
#define COLOR_H<span id="more-633"></span></p>
<p>/* &laquo;\033[1;38;5;2xx;48;5;2xxm\0&#8243; is 23 bytes */<br />
#define COLOR_MAXLEN 24</p>
<p>#define PERF_COLOR_NORMAL	&laquo;&raquo;<br />
#define PERF_COLOR_RESET	&laquo;\033[m&raquo;<br />
#define PERF_COLOR_BOLD		&laquo;\033[1m&raquo;<br />
#define PERF_COLOR_RED		&laquo;\033[31m&raquo;<br />
#define PERF_COLOR_GREEN	&laquo;\033[32m&raquo;<br />
#define PERF_COLOR_YELLOW	&laquo;\033[33m&raquo;<br />
#define PERF_COLOR_BLUE		&laquo;\033[34m&raquo;<br />
#define PERF_COLOR_MAGENTA	&laquo;\033[35m&raquo;<br />
#define PERF_COLOR_CYAN		&laquo;\033[36m&raquo;<br />
#define PERF_COLOR_BG_RED	&laquo;\033[41m&raquo;</p>
<p>#define MIN_GREEN	0.5<br />
#define MIN_RED		5.0</p>
<p>/*<br />
 * This variable stores the value of color.ui<br />
 */<br />
extern int perf_use_color_default;</p>
<p>/*<br />
 * Use this instead of perf_default_config if you need the value of color.ui.<br />
 */<br />
int perf_color_default_config(const char *var, const char *value, void *cb);</p>
<p>int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty);<br />
void color_parse(const char *value, const char *var, char *dst);<br />
void color_parse_mem(const char *value, int len, const char *var, char *dst);<br />
int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args);<br />
int color_fprintf(FILE *fp, const char *color, const char *fmt, &#8230;);<br />
int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, &#8230;);<br />
int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf);<br />
int percent_color_fprintf(FILE *fp, const char *fmt, double percent);<br />
const char *get_percent_color(double percent);</p>
<p>#endif /* COLOR_H */</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/color-h/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>color.c</title>
		<link>http://lynyrd.ru/color-c</link>
		<comments>http://lynyrd.ru/color-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:54:19 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=631</guid>
		<description><![CDATA[#include &#171;cache.h&#187;
#include &#171;color.h&#187;
int perf_use_color_default = -1;
static int parse_color(const char *name, int len)
{
	static const char * const color_names[] = {
		&#171;normal&#187;, &#171;black&#187;, &#171;red&#187;, &#171;green&#187;, &#171;yellow&#187;,
		&#171;blue&#187;, &#171;magenta&#187;, &#171;cyan&#187;, &#171;white&#187;
	};
	char *end;
	int i;
	for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {
		const char *str = color_names[i];
		if (!strncasecmp(name, str, len) &#038;&#038; !str[len])
			return i - 1;
	}
	i = strtol(name, &#038;end, 10);
	if (end - name ]]></description>
			<content:encoded><![CDATA[<p>#include &laquo;cache.h&raquo;<br />
#include &laquo;color.h&raquo;<span id="more-631"></span></p>
<p>int perf_use_color_default = -1;</p>
<p>static int parse_color(const char *name, int len)<br />
{<br />
	static const char * const color_names[] = {<br />
		&laquo;normal&raquo;, &laquo;black&raquo;, &laquo;red&raquo;, &laquo;green&raquo;, &laquo;yellow&raquo;,<br />
		&laquo;blue&raquo;, &laquo;magenta&raquo;, &laquo;cyan&raquo;, &laquo;white&raquo;<br />
	};<br />
	char *end;<br />
	int i;</p>
<p>	for (i = 0; i < (int)ARRAY_SIZE(color_names); i++) {<br />
		const char *str = color_names[i];<br />
		if (!strncasecmp(name, str, len) &#038;&#038; !str[len])<br />
			return i - 1;<br />
	}<br />
	i = strtol(name, &#038;end, 10);<br />
	if (end - name == len &#038;&#038; i >= -1 &#038;&#038; i <= 255)<br />
		return i;<br />
	return -2;<br />
}</p>
<p>static int parse_attr(const char *name, int len)<br />
{<br />
	static const int attr_values[] = { 1, 2, 4, 5, 7 };<br />
	static const char * const attr_names[] = {<br />
		"bold", "dim", "ul", "blink", "reverse"<br />
	};<br />
	unsigned int i;</p>
<p>	for (i = 0; i < ARRAY_SIZE(attr_names); i++) {<br />
		const char *str = attr_names[i];<br />
		if (!strncasecmp(name, str, len) &#038;&#038; !str[len])<br />
			return attr_values[i];<br />
	}<br />
	return -1;<br />
}</p>
<p>void color_parse(const char *value, const char *var, char *dst)<br />
{<br />
	color_parse_mem(value, strlen(value), var, dst);<br />
}</p>
<p>void color_parse_mem(const char *value, int value_len, const char *var,<br />
		char *dst)<br />
{<br />
	const char *ptr = value;<br />
	int len = value_len;<br />
	int attr = -1;<br />
	int fg = -2;<br />
	int bg = -2;</p>
<p>	if (!strncasecmp(value, "reset", len)) {<br />
		strcpy(dst, PERF_COLOR_RESET);<br />
		return;<br />
	}</p>
<p>	/* [fg [bg]] [attr] */<br />
	while (len > 0) {<br />
		const char *word = ptr;<br />
		int val, wordlen = 0;</p>
<p>		while (len > 0 &#038;&#038; !isspace(word[wordlen])) {<br />
			wordlen++;<br />
			len&#8211;;<br />
		}</p>
<p>		ptr = word + wordlen;<br />
		while (len > 0 &#038;&#038; isspace(*ptr)) {<br />
			ptr++;<br />
			len&#8211;;<br />
		}</p>
<p>		val = parse_color(word, wordlen);<br />
		if (val >= -1) {<br />
			if (fg == -2) {<br />
				fg = val;<br />
				continue;<br />
			}<br />
			if (bg == -2) {<br />
				bg = val;<br />
				continue;<br />
			}<br />
			goto bad;<br />
		}<br />
		val = parse_attr(word, wordlen);<br />
		if (val < 0 || attr != -1)<br />
			goto bad;<br />
		attr = val;<br />
	}</p>
<p>	if (attr >= 0 || fg >= 0 || bg >= 0) {<br />
		int sep = 0;</p>
<p>		*dst++ = &#8216;\033&#8242;;<br />
		*dst++ = &#8216;[&#8217;;<br />
		if (attr >= 0) {<br />
			*dst++ = &#8216;0&#8242; + attr;<br />
			sep++;<br />
		}<br />
		if (fg >= 0) {<br />
			if (sep++)<br />
				*dst++ = &#8216;;&#8217;;<br />
			if (fg < <img src='http://lynyrd.ru/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> {<br />
				*dst++ = '3';<br />
				*dst++ = '0' + fg;<br />
			} else {<br />
				dst += sprintf(dst, "38;5;%d", fg);<br />
			}<br />
		}<br />
		if (bg >= 0) {<br />
			if (sep++)<br />
				*dst++ = &#8216;;&#8217;;<br />
			if (bg < <img src='http://lynyrd.ru/wp-includes/images/smilies/icon_cool.gif' alt='8)' class='wp-smiley' /> {<br />
				*dst++ = '4';<br />
				*dst++ = '0' + bg;<br />
			} else {<br />
				dst += sprintf(dst, "48;5;%d", bg);<br />
			}<br />
		}<br />
		*dst++ = 'm';<br />
	}<br />
	*dst = 0;<br />
	return;<br />
bad:<br />
	die("bad color value '%.*s' for variable '%s'", value_len, value, var);<br />
}</p>
<p>int perf_config_colorbool(const char *var, const char *value, int stdout_is_tty)<br />
{<br />
	if (value) {<br />
		if (!strcasecmp(value, "never"))<br />
			return 0;<br />
		if (!strcasecmp(value, "always"))<br />
			return 1;<br />
		if (!strcasecmp(value, "auto"))<br />
			goto auto_color;<br />
	}</p>
<p>	/* Missing or explicit false to turn off colorization */<br />
	if (!perf_config_bool(var, value))<br />
		return 0;</p>
<p>	/* any normal truth value defaults to 'auto' */<br />
 auto_color:<br />
	if (stdout_is_tty < 0)<br />
		stdout_is_tty = isatty(1);<br />
	if (stdout_is_tty || (pager_in_use() &#038;&#038; pager_use_color)) {<br />
		char *term = getenv("TERM");<br />
		if (term &#038;&#038; strcmp(term, "dumb"))<br />
			return 1;<br />
	}<br />
	return 0;<br />
}</p>
<p>int perf_color_default_config(const char *var, const char *value, void *cb)<br />
{<br />
	if (!strcmp(var, "color.ui")) {<br />
		perf_use_color_default = perf_config_colorbool(var, value, -1);<br />
		return 0;<br />
	}</p>
<p>	return perf_default_config(var, value, cb);<br />
}</p>
<p>static int __color_vfprintf(FILE *fp, const char *color, const char *fmt,<br />
		va_list args, const char *trail)<br />
{<br />
	int r = 0;</p>
<p>	/*<br />
	 * Auto-detect:<br />
	 */<br />
	if (perf_use_color_default < 0) {<br />
		if (isatty(1) || pager_in_use())<br />
			perf_use_color_default = 1;<br />
		else<br />
			perf_use_color_default = 0;<br />
	}</p>
<p>	if (perf_use_color_default &#038;&#038; *color)<br />
		r += fprintf(fp, "%s", color);<br />
	r += vfprintf(fp, fmt, args);<br />
	if (perf_use_color_default &#038;&#038; *color)<br />
		r += fprintf(fp, "%s", PERF_COLOR_RESET);<br />
	if (trail)<br />
		r += fprintf(fp, "%s", trail);<br />
	return r;<br />
}</p>
<p>int color_vfprintf(FILE *fp, const char *color, const char *fmt, va_list args)<br />
{<br />
	return __color_vfprintf(fp, color, fmt, args, NULL);<br />
}</p>
<p>int color_fprintf(FILE *fp, const char *color, const char *fmt, ...)<br />
{<br />
	va_list args;<br />
	int r;</p>
<p>	va_start(args, fmt);<br />
	r = color_vfprintf(fp, color, fmt, args);<br />
	va_end(args);<br />
	return r;<br />
}</p>
<p>int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...)<br />
{<br />
	va_list args;<br />
	int r;<br />
	va_start(args, fmt);<br />
	r = __color_vfprintf(fp, color, fmt, args, "\n");<br />
	va_end(args);<br />
	return r;<br />
}</p>
<p>/*<br />
 * This function splits the buffer by newlines and colors the lines individually.<br />
 *<br />
 * Returns 0 on success.<br />
 */<br />
int color_fwrite_lines(FILE *fp, const char *color,<br />
		size_t count, const char *buf)<br />
{<br />
	if (!*color)<br />
		return fwrite(buf, count, 1, fp) != 1;</p>
<p>	while (count) {<br />
		char *p = memchr(buf, '\n', count);</p>
<p>		if (p != buf &#038;&#038; (fputs(color, fp) < 0 ||<br />
				fwrite(buf, p ? (size_t)(p - buf) : count, 1, fp) != 1 ||<br />
				fputs(PERF_COLOR_RESET, fp) < 0))<br />
			return -1;<br />
		if (!p)<br />
			return 0;<br />
		if (fputc('\n', fp) < 0)<br />
			return -1;<br />
		count -= p + 1 - buf;<br />
		buf = p + 1;<br />
	}<br />
	return 0;<br />
}</p>
<p>const char *get_percent_color(double percent)<br />
{<br />
	const char *color = PERF_COLOR_NORMAL;</p>
<p>	/*<br />
	 * We color high-overhead entries in red, mid-overhead<br />
	 * entries in green - and keep the low overhead places<br />
	 * normal:<br />
	 */<br />
	if (percent >= MIN_RED)<br />
		color = PERF_COLOR_RED;<br />
	else {<br />
		if (percent > MIN_GREEN)<br />
			color = PERF_COLOR_GREEN;<br />
	}<br />
	return color;<br />
}</p>
<p>int percent_color_fprintf(FILE *fp, const char *fmt, double percent)<br />
{<br />
	int r;<br />
	const char *color;</p>
<p>	color = get_percent_color(percent);<br />
	r = color_fprintf(fp, color, fmt, percent);</p>
<p>	return r;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/color-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>callchain.h</title>
		<link>http://lynyrd.ru/callchain-h</link>
		<comments>http://lynyrd.ru/callchain-h#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:52:36 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=629</guid>
		<description><![CDATA[#ifndef __PERF_CALLCHAIN_H
#define __PERF_CALLCHAIN_H
#include &#171;../perf.h&#187;
#include

#include

#include &#171;util.h&#187;
#include &#171;symbol.h&#187;
enum chain_mode {
	CHAIN_NONE,
	CHAIN_FLAT,
	CHAIN_GRAPH_ABS,
	CHAIN_GRAPH_REL
};
struct callchain_node {
	struct callchain_node	*parent;
	struct list_head	brothers;
	struct list_head	children;
	struct list_head	val;
	struct rb_node		rb_node; /* to sort nodes in an rbtree */
	struct rb_root		rb_root; /* sorted tree of children */
	unsigned int		val_nr;
	u64			hit;
	u64			children_hit;
};
struct callchain_param;
typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *,
				 u64, struct callchain_param *);
struct callchain_param {
	enum chain_mode 	mode;
	double			min_percent;
	sort_chain_func_t	sort;
};
struct callchain_list {
	u64			ip;
	struct symbol		*sym;
	struct list_head	list;
};
static inline void callchain_init(struct callchain_node ]]></description>
			<content:encoded><![CDATA[<p>#ifndef __PERF_CALLCHAIN_H<br />
#define __PERF_CALLCHAIN_H<span id="more-629"></span></p>
<p>#include &laquo;../perf.h&raquo;<br />
#include
<linux/list.h>
#include
<linux/rbtree.h>
#include &laquo;util.h&raquo;<br />
#include &laquo;symbol.h&raquo;</p>
<p>enum chain_mode {<br />
	CHAIN_NONE,<br />
	CHAIN_FLAT,<br />
	CHAIN_GRAPH_ABS,<br />
	CHAIN_GRAPH_REL<br />
};</p>
<p>struct callchain_node {<br />
	struct callchain_node	*parent;<br />
	struct list_head	brothers;<br />
	struct list_head	children;<br />
	struct list_head	val;<br />
	struct rb_node		rb_node; /* to sort nodes in an rbtree */<br />
	struct rb_root		rb_root; /* sorted tree of children */<br />
	unsigned int		val_nr;<br />
	u64			hit;<br />
	u64			children_hit;<br />
};</p>
<p>struct callchain_param;</p>
<p>typedef void (*sort_chain_func_t)(struct rb_root *, struct callchain_node *,<br />
				 u64, struct callchain_param *);</p>
<p>struct callchain_param {<br />
	enum chain_mode 	mode;<br />
	double			min_percent;<br />
	sort_chain_func_t	sort;<br />
};</p>
<p>struct callchain_list {<br />
	u64			ip;<br />
	struct symbol		*sym;<br />
	struct list_head	list;<br />
};</p>
<p>static inline void callchain_init(struct callchain_node *node)<br />
{<br />
	INIT_LIST_HEAD(&#038;node->brothers);<br />
	INIT_LIST_HEAD(&#038;node->children);<br />
	INIT_LIST_HEAD(&#038;node->val);<br />
}</p>
<p>static inline u64 cumul_hits(struct callchain_node *node)<br />
{<br />
	return node->hit + node->children_hit;<br />
}</p>
<p>int register_callchain_param(struct callchain_param *param);<br />
void append_chain(struct callchain_node *root, struct ip_callchain *chain,<br />
		  struct symbol **syms);<br />
#endif</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/callchain-h/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>callchain.c</title>
		<link>http://lynyrd.ru/callchain-c</link>
		<comments>http://lynyrd.ru/callchain-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:51:33 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=627</guid>
		<description><![CDATA[/*
 * Copyright (C) 2009, Frederic Weisbecker 
 *
 * Handle the callchains from the stream in an ad-hoc radix tree and then
 * sort them in an rbtree.
 *
 * Using a radix for code path provides a fast retrieval and factorizes
 * memory use. Also that lets us use the paths in a ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * Copyright (C) 2009, Frederic Weisbecker <fweisbec@gmail.com><span id="more-627"></span><br />
 *<br />
 * Handle the callchains from the stream in an ad-hoc radix tree and then<br />
 * sort them in an rbtree.<br />
 *<br />
 * Using a radix for code path provides a fast retrieval and factorizes<br />
 * memory use. Also that lets us use the paths in a hierarchical graph view.<br />
 *<br />
 */</p>
<p>#include <stdlib.h><br />
#include <stdio.h><br />
#include <stdbool.h><br />
#include <errno.h><br />
#include<br />
<math.h>
<p>#include &laquo;callchain.h&raquo;</p>
<p>#define chain_for_each_child(child, parent)	\<br />
	list_for_each_entry(child, &#038;parent->children, brothers)</p>
<p>static void<br />
rb_insert_callchain(struct rb_root *root, struct callchain_node *chain,<br />
		    enum chain_mode mode)<br />
{<br />
	struct rb_node **p = &#038;root->rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct callchain_node *rnode;<br />
	u64 chain_cumul = cumul_hits(chain);</p>
<p>	while (*p) {<br />
		u64 rnode_cumul;</p>
<p>		parent = *p;<br />
		rnode = rb_entry(parent, struct callchain_node, rb_node);<br />
		rnode_cumul = cumul_hits(rnode);</p>
<p>		switch (mode) {<br />
		case CHAIN_FLAT:<br />
			if (rnode->hit < chain->hit)<br />
				p = &#038;(*p)->rb_left;<br />
			else<br />
				p = &#038;(*p)->rb_right;<br />
			break;<br />
		case CHAIN_GRAPH_ABS: /* Falldown */<br />
		case CHAIN_GRAPH_REL:<br />
			if (rnode_cumul < chain_cumul)<br />
				p = &#038;(*p)->rb_left;<br />
			else<br />
				p = &#038;(*p)->rb_right;<br />
			break;<br />
		case CHAIN_NONE:<br />
		default:<br />
			break;<br />
		}<br />
	}</p>
<p>	rb_link_node(&#038;chain->rb_node, parent, p);<br />
	rb_insert_color(&#038;chain->rb_node, root);<br />
}</p>
<p>static void<br />
__sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,<br />
		  u64 min_hit)<br />
{<br />
	struct callchain_node *child;</p>
<p>	chain_for_each_child(child, node)<br />
		__sort_chain_flat(rb_root, child, min_hit);</p>
<p>	if (node->hit &#038;&#038; node->hit >= min_hit)<br />
		rb_insert_callchain(rb_root, node, CHAIN_FLAT);<br />
}</p>
<p>/*<br />
 * Once we get every callchains from the stream, we can now<br />
 * sort them by hit<br />
 */<br />
static void<br />
sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,<br />
		u64 min_hit, struct callchain_param *param __used)<br />
{<br />
	__sort_chain_flat(rb_root, node, min_hit);<br />
}</p>
<p>static void __sort_chain_graph_abs(struct callchain_node *node,<br />
				   u64 min_hit)<br />
{<br />
	struct callchain_node *child;</p>
<p>	node->rb_root = RB_ROOT;</p>
<p>	chain_for_each_child(child, node) {<br />
		__sort_chain_graph_abs(child, min_hit);<br />
		if (cumul_hits(child) >= min_hit)<br />
			rb_insert_callchain(&#038;node->rb_root, child,<br />
					    CHAIN_GRAPH_ABS);<br />
	}<br />
}</p>
<p>static void<br />
sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_node *chain_root,<br />
		     u64 min_hit, struct callchain_param *param __used)<br />
{<br />
	__sort_chain_graph_abs(chain_root, min_hit);<br />
	rb_root->rb_node = chain_root->rb_root.rb_node;<br />
}</p>
<p>static void __sort_chain_graph_rel(struct callchain_node *node,<br />
				   double min_percent)<br />
{<br />
	struct callchain_node *child;<br />
	u64 min_hit;</p>
<p>	node->rb_root = RB_ROOT;<br />
	min_hit = ceil(node->children_hit * min_percent);</p>
<p>	chain_for_each_child(child, node) {<br />
		__sort_chain_graph_rel(child, min_percent);<br />
		if (cumul_hits(child) >= min_hit)<br />
			rb_insert_callchain(&#038;node->rb_root, child,<br />
					    CHAIN_GRAPH_REL);<br />
	}<br />
}</p>
<p>static void<br />
sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_node *chain_root,<br />
		     u64 min_hit __used, struct callchain_param *param)<br />
{<br />
	__sort_chain_graph_rel(chain_root, param->min_percent / 100.0);<br />
	rb_root->rb_node = chain_root->rb_root.rb_node;<br />
}</p>
<p>int register_callchain_param(struct callchain_param *param)<br />
{<br />
	switch (param->mode) {<br />
	case CHAIN_GRAPH_ABS:<br />
		param->sort = sort_chain_graph_abs;<br />
		break;<br />
	case CHAIN_GRAPH_REL:<br />
		param->sort = sort_chain_graph_rel;<br />
		break;<br />
	case CHAIN_FLAT:<br />
		param->sort = sort_chain_flat;<br />
		break;<br />
	case CHAIN_NONE:<br />
	default:<br />
		return -1;<br />
	}<br />
	return 0;<br />
}</p>
<p>/*<br />
 * Create a child for a parent. If inherit_children, then the new child<br />
 * will become the new parent of it&#8217;s parent children<br />
 */<br />
static struct callchain_node *<br />
create_child(struct callchain_node *parent, bool inherit_children)<br />
{<br />
	struct callchain_node *new;</p>
<p>	new = malloc(sizeof(*new));<br />
	if (!new) {<br />
		perror(&raquo;not enough memory to create child for code path tree&raquo;);<br />
		return NULL;<br />
	}<br />
	new->parent = parent;<br />
	INIT_LIST_HEAD(&#038;new->children);<br />
	INIT_LIST_HEAD(&#038;new->val);</p>
<p>	if (inherit_children) {<br />
		struct callchain_node *next;</p>
<p>		list_splice(&#038;parent->children, &#038;new->children);<br />
		INIT_LIST_HEAD(&#038;parent->children);</p>
<p>		chain_for_each_child(next, new)<br />
			next->parent = new;<br />
	}<br />
	list_add_tail(&#038;new->brothers, &#038;parent->children);</p>
<p>	return new;<br />
}</p>
<p>/*<br />
 * Fill the node with callchain values<br />
 */<br />
static void<br />
fill_node(struct callchain_node *node, struct ip_callchain *chain,<br />
	  int start, struct symbol **syms)<br />
{<br />
	unsigned int i;</p>
<p>	for (i = start; i < chain->nr; i++) {<br />
		struct callchain_list *call;</p>
<p>		call = malloc(sizeof(*call));<br />
		if (!call) {<br />
			perror(&raquo;not enough memory for the code path tree&raquo;);<br />
			return;<br />
		}<br />
		call->ip = chain->ips[i];<br />
		call->sym = syms[i];<br />
		list_add_tail(&#038;call->list, &#038;node->val);<br />
	}<br />
	node->val_nr = chain->nr &#8211; start;<br />
	if (!node->val_nr)<br />
		printf(&raquo;Warning: empty node in callchain tree\n&raquo;);<br />
}</p>
<p>static void<br />
add_child(struct callchain_node *parent, struct ip_callchain *chain,<br />
	  int start, struct symbol **syms)<br />
{<br />
	struct callchain_node *new;</p>
<p>	new = create_child(parent, false);<br />
	fill_node(new, chain, start, syms);</p>
<p>	new->children_hit = 0;<br />
	new->hit = 1;<br />
}</p>
<p>/*<br />
 * Split the parent in two parts (a new child is created) and<br />
 * give a part of its callchain to the created child.<br />
 * Then create another child to host the given callchain of new branch<br />
 */<br />
static void<br />
split_add_child(struct callchain_node *parent, struct ip_callchain *chain,<br />
		struct callchain_list *to_split, int idx_parents, int idx_local,<br />
		struct symbol **syms)<br />
{<br />
	struct callchain_node *new;<br />
	struct list_head *old_tail;<br />
	unsigned int idx_total = idx_parents + idx_local;</p>
<p>	/* split */<br />
	new = create_child(parent, true);</p>
<p>	/* split the callchain and move a part to the new child */<br />
	old_tail = parent->val.prev;<br />
	list_del_range(&#038;to_split->list, old_tail);<br />
	new->val.next = &#038;to_split->list;<br />
	new->val.prev = old_tail;<br />
	to_split->list.prev = &#038;new->val;<br />
	old_tail->next = &#038;new->val;</p>
<p>	/* split the hits */<br />
	new->hit = parent->hit;<br />
	new->children_hit = parent->children_hit;<br />
	parent->children_hit = cumul_hits(new);<br />
	new->val_nr = parent->val_nr &#8211; idx_local;<br />
	parent->val_nr = idx_local;</p>
<p>	/* create a new child for the new branch if any */<br />
	if (idx_total < chain->nr) {<br />
		parent->hit = 0;<br />
		add_child(parent, chain, idx_total, syms);<br />
		parent->children_hit++;<br />
	} else {<br />
		parent->hit = 1;<br />
	}<br />
}</p>
<p>static int<br />
__append_chain(struct callchain_node *root, struct ip_callchain *chain,<br />
	       unsigned int start, struct symbol **syms);</p>
<p>static void<br />
__append_chain_children(struct callchain_node *root, struct ip_callchain *chain,<br />
			struct symbol **syms, unsigned int start)<br />
{<br />
	struct callchain_node *rnode;</p>
<p>	/* lookup in childrens */<br />
	chain_for_each_child(rnode, root) {<br />
		unsigned int ret = __append_chain(rnode, chain, start, syms);</p>
<p>		if (!ret)<br />
			goto inc_children_hit;<br />
	}<br />
	/* nothing in children, add to the current node */<br />
	add_child(root, chain, start, syms);</p>
<p>inc_children_hit:<br />
	root->children_hit++;<br />
}</p>
<p>static int<br />
__append_chain(struct callchain_node *root, struct ip_callchain *chain,<br />
	       unsigned int start, struct symbol **syms)<br />
{<br />
	struct callchain_list *cnode;<br />
	unsigned int i = start;<br />
	bool found = false;</p>
<p>	/*<br />
	 * Lookup in the current node<br />
	 * If we have a symbol, then compare the start to match<br />
	 * anywhere inside a function.<br />
	 */<br />
	list_for_each_entry(cnode, &#038;root->val, list) {<br />
		if (i == chain->nr)<br />
			break;<br />
		if (cnode->sym &#038;&#038; syms[i]) {<br />
			if (cnode->sym->start != syms[i]->start)<br />
				break;<br />
		} else if (cnode->ip != chain->ips[i])<br />
			break;<br />
		if (!found)<br />
			found = true;<br />
		i++;<br />
	}</p>
<p>	/* matches not, relay on the parent */<br />
	if (!found)<br />
		return -1;</p>
<p>	/* we match only a part of the node. Split it and add the new chain */<br />
	if (i &#8211; start < root->val_nr) {<br />
		split_add_child(root, chain, cnode, start, i &#8211; start, syms);<br />
		return 0;<br />
	}</p>
<p>	/* we match 100% of the path, increment the hit */<br />
	if (i &#8211; start == root->val_nr &#038;&#038; i == chain->nr) {<br />
		root->hit++;<br />
		return 0;<br />
	}</p>
<p>	/* We match the node and still have a part remaining */<br />
	__append_chain_children(root, chain, syms, i);</p>
<p>	return 0;<br />
}</p>
<p>void append_chain(struct callchain_node *root, struct ip_callchain *chain,<br />
		  struct symbol **syms)<br />
{<br />
	if (!chain->nr)<br />
		return;<br />
	__append_chain_children(root, chain, syms, 0);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/callchain-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>alias.c</title>
		<link>http://lynyrd.ru/alias-c</link>
		<comments>http://lynyrd.ru/alias-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:49:32 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/alias-c</guid>
		<description><![CDATA[#include &#171;cache.h&#187;
static const char *alias_key;
static char *alias_val;
static int alias_lookup_cb(const char *k, const char *v, void *cb __used)
{
	if (!prefixcmp(k, &#171;alias.&#187;) &#038;&#038; !strcmp(k+6, alias_key)) {
		if (!v)
			return config_error_nonbool(k);
		alias_val = strdup(v);
		return 0;
	}
	return 0;
}
char *alias_lookup(const char *alias)
{
	alias_key = alias;
	alias_val = NULL;
	perf_config(alias_lookup_cb, NULL);
	return alias_val;
}
int split_cmdline(char *cmdline, const char ***argv)
{
	int src, dst, count = 0, size = 16;
	char quoted = 0;
	*argv = ]]></description>
			<content:encoded><![CDATA[<p>#include &laquo;cache.h&raquo;<span id="more-626"></span></p>
<p>static const char *alias_key;<br />
static char *alias_val;</p>
<p>static int alias_lookup_cb(const char *k, const char *v, void *cb __used)<br />
{<br />
	if (!prefixcmp(k, &laquo;alias.&raquo;) &#038;&#038; !strcmp(k+6, alias_key)) {<br />
		if (!v)<br />
			return config_error_nonbool(k);<br />
		alias_val = strdup(v);<br />
		return 0;<br />
	}<br />
	return 0;<br />
}</p>
<p>char *alias_lookup(const char *alias)<br />
{<br />
	alias_key = alias;<br />
	alias_val = NULL;<br />
	perf_config(alias_lookup_cb, NULL);<br />
	return alias_val;<br />
}</p>
<p>int split_cmdline(char *cmdline, const char ***argv)<br />
{<br />
	int src, dst, count = 0, size = 16;<br />
	char quoted = 0;</p>
<p>	*argv = malloc(sizeof(char*) * size);</p>
<p>	/* split alias_string */<br />
	(*argv)[count++] = cmdline;<br />
	for (src = dst = 0; cmdline[src];) {<br />
		char c = cmdline[src];<br />
		if (!quoted &#038;&#038; isspace(c)) {<br />
			cmdline[dst++] = 0;<br />
			while (cmdline[++src]<br />
					&#038;&#038; isspace(cmdline[src]))<br />
				; /* skip */<br />
			if (count >= size) {<br />
				size += 16;<br />
				*argv = realloc(*argv, sizeof(char*) * size);<br />
			}<br />
			(*argv)[count++] = cmdline + dst;<br />
		} else if (!quoted &#038;&#038; (c == &#8216;\&raquo; || c == &#8216;&raquo;&#8216;)) {<br />
			quoted = c;<br />
			src++;<br />
		} else if (c == quoted) {<br />
			quoted = 0;<br />
			src++;<br />
		} else {<br />
			if (c == &#8216;\\&#8217; &#038;&#038; quoted != &#8216;\&raquo;) {<br />
				src++;<br />
				c = cmdline[src];<br />
				if (!c) {<br />
					free(*argv);<br />
					*argv = NULL;<br />
					return error(&raquo;cmdline ends with \\&raquo;);<br />
				}<br />
			}<br />
			cmdline[dst++] = c;<br />
			src++;<br />
		}<br />
	}</p>
<p>	cmdline[dst] = 0;</p>
<p>	if (quoted) {<br />
		free(*argv);<br />
		*argv = NULL;<br />
		return error(&raquo;unclosed quote&raquo;);<br />
	}</p>
<p>	return count;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/alias-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>abspath.c</title>
		<link>http://lynyrd.ru/abspath-c</link>
		<comments>http://lynyrd.ru/abspath-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:46:40 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/abspath-c</guid>
		<description><![CDATA[#include &#171;cache.h&#187;
/*
 * Do not use this for inspecting *tracked* content.  When path is a
 * symlink to a directory, we do not want to say it is a directory when
 * dealing with tracked content in the working tree.
 */
static int is_directory(const char *path)
{
	struct stat st;
	return (!stat(path, &#038;st) &#038;&#038; S_ISDIR(st.st_mode));
}
/* We allow &#171;recursive&#187; ]]></description>
			<content:encoded><![CDATA[<p>#include &laquo;cache.h&raquo;<span id="more-624"></span></p>
<p>/*<br />
 * Do not use this for inspecting *tracked* content.  When path is a<br />
 * symlink to a directory, we do not want to say it is a directory when<br />
 * dealing with tracked content in the working tree.<br />
 */<br />
static int is_directory(const char *path)<br />
{<br />
	struct stat st;<br />
	return (!stat(path, &#038;st) &#038;&#038; S_ISDIR(st.st_mode));<br />
}</p>
<p>/* We allow &laquo;recursive&raquo; symbolic links. Only within reason, though. */<br />
#define MAXDEPTH 5</p>
<p>const char *make_absolute_path(const char *path)<br />
{<br />
	static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];<br />
	char cwd[1024] = &laquo;&raquo;;<br />
	int buf_index = 1, len;</p>
<p>	int depth = MAXDEPTH;<br />
	char *last_elem = NULL;<br />
	struct stat st;</p>
<p>	if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)<br />
		die (&raquo;Too long path: %.*s&raquo;, 60, path);</p>
<p>	while (depth&#8211;) {<br />
		if (!is_directory(buf)) {<br />
			char *last_slash = strrchr(buf, &#8216;/&#8217;);<br />
			if (last_slash) {<br />
				*last_slash = &#8216;\0&#8242;;<br />
				last_elem = xstrdup(last_slash + 1);<br />
			} else {<br />
				last_elem = xstrdup(buf);<br />
				*buf = &#8216;\0&#8242;;<br />
			}<br />
		}</p>
<p>		if (*buf) {<br />
			if (!*cwd &#038;&#038; !getcwd(cwd, sizeof(cwd)))<br />
				die (&raquo;Could not get current working directory&raquo;);</p>
<p>			if (chdir(buf))<br />
				die (&raquo;Could not switch to &#8216;%s&#8217;&raquo;, buf);<br />
		}<br />
		if (!getcwd(buf, PATH_MAX))<br />
			die (&raquo;Could not get current working directory&raquo;);</p>
<p>		if (last_elem) {<br />
			len = strlen(buf);</p>
<p>			if (len + strlen(last_elem) + 2 > PATH_MAX)<br />
				die (&raquo;Too long path name: &#8216;%s/%s&#8217;&raquo;,<br />
						buf, last_elem);<br />
			buf[len] = &#8216;/&#8217;;<br />
			strcpy(buf + len + 1, last_elem);<br />
			free(last_elem);<br />
			last_elem = NULL;<br />
		}</p>
<p>		if (!lstat(buf, &#038;st) &#038;&#038; S_ISLNK(st.st_mode)) {<br />
			len = readlink(buf, next_buf, PATH_MAX);<br />
			if (len < 0)<br />
				die ("Invalid symlink: %s", buf);<br />
			if (PATH_MAX <= len)<br />
				die("symbolic link too long: %s", buf);<br />
			next_buf[len] = '\0';<br />
			buf = next_buf;<br />
			buf_index = 1 - buf_index;<br />
			next_buf = bufs[buf_index];<br />
		} else<br />
			break;<br />
	}</p>
<p>	if (*cwd &#038;&#038; chdir(cwd))<br />
		die ("Could not change back to '%s'", cwd);</p>
<p>	return buf;<br />
}</p>
<p>static const char *get_pwd_cwd(void)<br />
{<br />
	static char cwd[PATH_MAX + 1];<br />
	char *pwd;<br />
	struct stat cwd_stat, pwd_stat;<br />
	if (getcwd(cwd, PATH_MAX) == NULL)<br />
		return NULL;<br />
	pwd = getenv("PWD");<br />
	if (pwd &#038;&#038; strcmp(pwd, cwd)) {<br />
		stat(cwd, &#038;cwd_stat);<br />
		if (!stat(pwd, &#038;pwd_stat) &#038;&#038;<br />
		    pwd_stat.st_dev == cwd_stat.st_dev &#038;&#038;<br />
		    pwd_stat.st_ino == cwd_stat.st_ino) {<br />
			strlcpy(cwd, pwd, PATH_MAX);<br />
		}<br />
	}<br />
	return cwd;<br />
}</p>
<p>const char *make_nonrelative_path(const char *path)<br />
{<br />
	static char buf[PATH_MAX + 1];</p>
<p>	if (is_absolute_path(path)) {<br />
		if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)<br />
			die(&raquo;Too long path: %.*s&raquo;, 60, path);<br />
	} else {<br />
		const char *cwd = get_pwd_cwd();<br />
		if (!cwd)<br />
			die(&raquo;Cannot determine the current working directory&raquo;);<br />
		if (snprintf(buf, PATH_MAX, &laquo;%s/%s&raquo;, cwd, path) >= PATH_MAX)<br />
			die(&raquo;Too long path: %.*s&raquo;, 60, path);<br />
	}<br />
	return buf;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/abspath-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>perf.c</title>
		<link>http://lynyrd.ru/perf-c</link>
		<comments>http://lynyrd.ru/perf-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:43:57 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/perf-c</guid>
		<description><![CDATA[/*
 * perf.c
 *
 * Performance analysis utility.
 *
 * This is the main hub from which the sub-commands (perf stat,
 * perf top, perf record, perf report, etc.) are started.
 */
#include &#171;builtin.h&#187;
#include &#171;util/exec_cmd.h&#187;
#include &#171;util/cache.h&#187;
#include &#171;util/quote.h&#187;
#include &#171;util/run-command.h&#187;
#include &#171;util/parse-events.h&#187;
#include &#171;util/string.h&#187;
const char perf_usage_string[] =
	&#171;perf [--version] [--help] COMMAND [ARGS]&#171;;
const char perf_more_info_string[] =
	&#171;See &#8216;perf help COMMAND&#8217; for more information ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * perf.c<span id="more-622"></span><br />
 *<br />
 * Performance analysis utility.<br />
 *<br />
 * This is the main hub from which the sub-commands (perf stat,<br />
 * perf top, perf record, perf report, etc.) are started.<br />
 */<br />
#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;util/exec_cmd.h&raquo;<br />
#include &laquo;util/cache.h&raquo;<br />
#include &laquo;util/quote.h&raquo;<br />
#include &laquo;util/run-command.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;<br />
#include &laquo;util/string.h&raquo;</p>
<p>const char perf_usage_string[] =<br />
	&laquo;perf [--version] [--help] COMMAND [ARGS]&laquo;;</p>
<p>const char perf_more_info_string[] =<br />
	&laquo;See &#8216;perf help COMMAND&#8217; for more information on a specific command.&raquo;;</p>
<p>static int use_pager = -1;<br />
struct pager_config {<br />
	const char *cmd;<br />
	int val;<br />
};</p>
<p>static char debugfs_mntpt[MAXPATHLEN];</p>
<p>static int pager_command_config(const char *var, const char *value, void *data)<br />
{<br />
	struct pager_config *c = data;<br />
	if (!prefixcmp(var, &laquo;pager.&raquo;) &#038;&#038; !strcmp(var + 6, c->cmd))<br />
		c->val = perf_config_bool(var, value);<br />
	return 0;<br />
}</p>
<p>/* returns 0 for &laquo;no pager&raquo;, 1 for &laquo;use pager&raquo;, and -1 for &laquo;not specified&raquo; */<br />
int check_pager_config(const char *cmd)<br />
{<br />
	struct pager_config c;<br />
	c.cmd = cmd;<br />
	c.val = -1;<br />
	perf_config(pager_command_config, &#038;c);<br />
	return c.val;<br />
}</p>
<p>static void commit_pager_choice(void) {<br />
	switch (use_pager) {<br />
	case 0:<br />
		setenv(&raquo;PERF_PAGER&raquo;, &laquo;cat&raquo;, 1);<br />
		break;<br />
	case 1:<br />
		/* setup_pager(); */<br />
		break;<br />
	default:<br />
		break;<br />
	}<br />
}</p>
<p>static void set_debugfs_path(void)<br />
{<br />
	char *path;</p>
<p>	path = getenv(PERF_DEBUGFS_ENVIRONMENT);<br />
	snprintf(debugfs_path, MAXPATHLEN, &laquo;%s/%s&raquo;, path ?: debugfs_mntpt,<br />
		 &laquo;tracing/events&raquo;);<br />
}</p>
<p>static int handle_options(const char*** argv, int* argc, int* envchanged)<br />
{<br />
	int handled = 0;</p>
<p>	while (*argc > 0) {<br />
		const char *cmd = (*argv)[0];<br />
		if (cmd[0] != &#8216;-&#8217;)<br />
			break;</p>
<p>		/*<br />
		 * For legacy reasons, the &laquo;version&raquo; and &laquo;help&raquo;<br />
		 * commands can be written with &laquo;&#8211;&raquo; prepended<br />
		 * to make them look like flags.<br />
		 */<br />
		if (!strcmp(cmd, &laquo;&#8211;help&raquo;) || !strcmp(cmd, &laquo;&#8211;version&raquo;))<br />
			break;</p>
<p>		/*<br />
		 * Check remaining flags.<br />
		 */<br />
		if (!prefixcmp(cmd, &laquo;&#8211;exec-path&raquo;)) {<br />
			cmd += 11;<br />
			if (*cmd == &#8216;=&#8217;)<br />
				perf_set_argv_exec_path(cmd + 1);<br />
			else {<br />
				puts(perf_exec_path());<br />
				exit(0);<br />
			}<br />
		} else if (!strcmp(cmd, &laquo;&#8211;html-path&raquo;)) {<br />
			puts(system_path(PERF_HTML_PATH));<br />
			exit(0);<br />
		} else if (!strcmp(cmd, &laquo;-p&raquo;) || !strcmp(cmd, &laquo;&#8211;paginate&raquo;)) {<br />
			use_pager = 1;<br />
		} else if (!strcmp(cmd, &laquo;&#8211;no-pager&raquo;)) {<br />
			use_pager = 0;<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
		} else if (!strcmp(cmd, &laquo;&#8211;perf-dir&raquo;)) {<br />
			if (*argc < 2) {<br />
				fprintf(stderr, "No directory given for --perf-dir.\n" );<br />
				usage(perf_usage_string);<br />
			}<br />
			setenv(PERF_DIR_ENVIRONMENT, (*argv)[1], 1);<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
			(*argv)++;<br />
			(*argc)--;<br />
			handled++;<br />
		} else if (!prefixcmp(cmd, "--perf-dir=")) {<br />
			setenv(PERF_DIR_ENVIRONMENT, cmd + 10, 1);<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
		} else if (!strcmp(cmd, "--work-tree")) {<br />
			if (*argc < 2) {<br />
				fprintf(stderr, "No directory given for --work-tree.\n" );<br />
				usage(perf_usage_string);<br />
			}<br />
			setenv(PERF_WORK_TREE_ENVIRONMENT, (*argv)[1], 1);<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
			(*argv)++;<br />
			(*argc)--;<br />
		} else if (!prefixcmp(cmd, "--work-tree=")) {<br />
			setenv(PERF_WORK_TREE_ENVIRONMENT, cmd + 12, 1);<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
		} else if (!strcmp(cmd, "--debugfs-dir")) {<br />
			if (*argc < 2) {<br />
				fprintf(stderr, "No directory given for --debugfs-dir.\n");<br />
				usage(perf_usage_string);<br />
			}<br />
			strncpy(debugfs_mntpt, (*argv)[1], MAXPATHLEN);<br />
			debugfs_mntpt[MAXPATHLEN - 1] = '\0';<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
			(*argv)++;<br />
			(*argc)--;<br />
		} else if (!prefixcmp(cmd, "--debugfs-dir=")) {<br />
			strncpy(debugfs_mntpt, cmd + 14, MAXPATHLEN);<br />
			debugfs_mntpt[MAXPATHLEN - 1] = '\0';<br />
			if (envchanged)<br />
				*envchanged = 1;<br />
		} else {<br />
			fprintf(stderr, "Unknown option: %s\n", cmd);<br />
			usage(perf_usage_string);<br />
		}</p>
<p>		(*argv)++;<br />
		(*argc)--;<br />
		handled++;<br />
	}<br />
	return handled;<br />
}</p>
<p>static int handle_alias(int *argcp, const char ***argv)<br />
{<br />
	int envchanged = 0, ret = 0, saved_errno = errno;<br />
	int count, option_count;<br />
	const char** new_argv;<br />
	const char *alias_command;<br />
	char *alias_string;</p>
<p>	alias_command = (*argv)[0];<br />
	alias_string = alias_lookup(alias_command);<br />
	if (alias_string) {<br />
		if (alias_string[0] == '!') {<br />
			if (*argcp > 1) {<br />
				struct strbuf buf;</p>
<p>				strbuf_init(&#038;buf, PATH_MAX);<br />
				strbuf_addstr(&#038;buf, alias_string);<br />
				sq_quote_argv(&#038;buf, (*argv) + 1, PATH_MAX);<br />
				free(alias_string);<br />
				alias_string = buf.buf;<br />
			}<br />
			ret = system(alias_string + 1);<br />
			if (ret >= 0 &#038;&#038; WIFEXITED(ret) &#038;&#038;<br />
			    WEXITSTATUS(ret) != 127)<br />
				exit(WEXITSTATUS(ret));<br />
			die(&raquo;Failed to run &#8216;%s&#8217; when expanding alias &#8216;%s&#8217;&raquo;,<br />
			    alias_string + 1, alias_command);<br />
		}<br />
		count = split_cmdline(alias_string, &#038;new_argv);<br />
		if (count < 0)<br />
			die("Bad alias.%s string", alias_command);<br />
		option_count = handle_options(&#038;new_argv, &#038;count, &#038;envchanged);<br />
		if (envchanged)<br />
			die("alias '%s' changes environment variables\n"<br />
				 "You can use '!perf' in the alias to do this.",<br />
				 alias_command);<br />
		memmove(new_argv - option_count, new_argv,<br />
				count * sizeof(char *));<br />
		new_argv -= option_count;</p>
<p>		if (count < 1)<br />
			die("empty alias for %s", alias_command);</p>
<p>		if (!strcmp(alias_command, new_argv[0]))<br />
			die("recursive alias: %s", alias_command);</p>
<p>		new_argv = realloc(new_argv, sizeof(char*) *<br />
				    (count + *argcp + 1));<br />
		/* insert after command name */<br />
		memcpy(new_argv + count, *argv + 1, sizeof(char*) * *argcp);<br />
		new_argv[count+*argcp] = NULL;</p>
<p>		*argv = new_argv;<br />
		*argcp += count - 1;</p>
<p>		ret = 1;<br />
	}</p>
<p>	errno = saved_errno;</p>
<p>	return ret;<br />
}</p>
<p>const char perf_version_string[] = PERF_VERSION;</p>
<p>#define RUN_SETUP	(1<<0)<br />
#define USE_PAGER	(1<<1)<br />
/*<br />
 * require working tree to be present -- anything uses this needs<br />
 * RUN_SETUP for reading from the configuration file.<br />
 */<br />
#define NEED_WORK_TREE	(1<<2)</p>
<p>struct cmd_struct {<br />
	const char *cmd;<br />
	int (*fn)(int, const char **, const char *);<br />
	int option;<br />
};</p>
<p>static int run_builtin(struct cmd_struct *p, int argc, const char **argv)<br />
{<br />
	int status;<br />
	struct stat st;<br />
	const char *prefix;</p>
<p>	prefix = NULL;<br />
	if (p->option &#038; RUN_SETUP)<br />
		prefix = NULL; /* setup_perf_directory(); */</p>
<p>	if (use_pager == -1 &#038;&#038; p->option &#038; RUN_SETUP)<br />
		use_pager = check_pager_config(p->cmd);<br />
	if (use_pager == -1 &#038;&#038; p->option &#038; USE_PAGER)<br />
		use_pager = 1;<br />
	commit_pager_choice();<br />
	set_debugfs_path();</p>
<p>	status = p->fn(argc, argv, prefix);<br />
	if (status)<br />
		return status &#038; 0xff;</p>
<p>	/* Somebody closed stdout? */<br />
	if (fstat(fileno(stdout), &#038;st))<br />
		return 0;<br />
	/* Ignore write errors for pipes and sockets.. */<br />
	if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode))<br />
		return 0;</p>
<p>	/* Check for ENOSPC and EIO errors.. */<br />
	if (fflush(stdout))<br />
		die(&raquo;write failure on standard output: %s&raquo;, strerror(errno));<br />
	if (ferror(stdout))<br />
		die(&raquo;unknown write failure on standard output&raquo;);<br />
	if (fclose(stdout))<br />
		die(&raquo;close failed on standard output: %s&raquo;, strerror(errno));<br />
	return 0;<br />
}</p>
<p>static void handle_internal_command(int argc, const char **argv)<br />
{<br />
	const char *cmd = argv[0];<br />
	static struct cmd_struct commands[] = {<br />
		{ &laquo;help&raquo;, cmd_help, 0 },<br />
		{ &laquo;list&raquo;, cmd_list, 0 },<br />
		{ &laquo;record&raquo;, cmd_record, 0 },<br />
		{ &laquo;report&raquo;, cmd_report, 0 },<br />
		{ &laquo;stat&raquo;, cmd_stat, 0 },<br />
		{ &laquo;timechart&raquo;, cmd_timechart, 0 },<br />
		{ &laquo;top&raquo;, cmd_top, 0 },<br />
		{ &laquo;annotate&raquo;, cmd_annotate, 0 },<br />
		{ &laquo;version&raquo;, cmd_version, 0 },<br />
		{ &laquo;trace&raquo;, cmd_trace, 0 },<br />
		{ &laquo;sched&raquo;, cmd_sched, 0 },<br />
	};<br />
	unsigned int i;<br />
	static const char ext[] = STRIP_EXTENSION;</p>
<p>	if (sizeof(ext) > 1) {<br />
		i = strlen(argv[0]) &#8211; strlen(ext);<br />
		if (i > 0 &#038;&#038; !strcmp(argv[0] + i, ext)) {<br />
			char *argv0 = strdup(argv[0]);<br />
			argv[0] = cmd = argv0;<br />
			argv0[i] = &#8216;\0&#8242;;<br />
		}<br />
	}</p>
<p>	/* Turn &laquo;perf cmd &#8211;help&raquo; into &laquo;perf help cmd&raquo; */<br />
	if (argc > 1 &#038;&#038; !strcmp(argv[1], &laquo;&#8211;help&raquo;)) {<br />
		argv[1] = argv[0];<br />
		argv[0] = cmd = &laquo;help&raquo;;<br />
	}</p>
<p>	for (i = 0; i < ARRAY_SIZE(commands); i++) {<br />
		struct cmd_struct *p = commands+i;<br />
		if (strcmp(p->cmd, cmd))<br />
			continue;<br />
		exit(run_builtin(p, argc, argv));<br />
	}<br />
}</p>
<p>static void execv_dashed_external(const char **argv)<br />
{<br />
	struct strbuf cmd = STRBUF_INIT;<br />
	const char *tmp;<br />
	int status;</p>
<p>	strbuf_addf(&#038;cmd, &laquo;perf-%s&raquo;, argv[0]);</p>
<p>	/*<br />
	 * argv[0] must be the perf command, but the argv array<br />
	 * belongs to the caller, and may be reused in<br />
	 * subsequent loop iterations. Save argv[0] and<br />
	 * restore it on error.<br />
	 */<br />
	tmp = argv[0];<br />
	argv[0] = cmd.buf;</p>
<p>	/*<br />
	 * if we fail because the command is not found, it is<br />
	 * OK to return. Otherwise, we just pass along the status code.<br />
	 */<br />
	status = run_command_v_opt(argv, 0);<br />
	if (status != -ERR_RUN_COMMAND_EXEC) {<br />
		if (IS_RUN_COMMAND_ERR(status))<br />
			die(&raquo;unable to run &#8216;%s&#8217;&raquo;, argv[0]);<br />
		exit(-status);<br />
	}<br />
	errno = ENOENT; /* as if we called execvp */</p>
<p>	argv[0] = tmp;</p>
<p>	strbuf_release(&#038;cmd);<br />
}</p>
<p>static int run_argv(int *argcp, const char ***argv)<br />
{<br />
	int done_alias = 0;</p>
<p>	while (1) {<br />
		/* See if it&#8217;s an internal command */<br />
		handle_internal_command(*argcp, *argv);</p>
<p>		/* .. then try the external ones */<br />
		execv_dashed_external(*argv);</p>
<p>		/* It could be an alias &#8212; this works around the insanity<br />
		 * of overriding &laquo;perf log&raquo; with &laquo;perf show&raquo; by having<br />
		 * alias.log = show<br />
		 */<br />
		if (done_alias || !handle_alias(argcp, argv))<br />
			break;<br />
		done_alias = 1;<br />
	}</p>
<p>	return done_alias;<br />
}</p>
<p>/* mini /proc/mounts parser: searching for &laquo;^blah /mount/point debugfs&raquo; */<br />
static void get_debugfs_mntpt(void)<br />
{<br />
	FILE *file;<br />
	char fs_type[100];<br />
	char debugfs[MAXPATHLEN];</p>
<p>	/*<br />
	 * try the standard location<br />
	 */<br />
	if (valid_debugfs_mount(&raquo;/sys/kernel/debug/&raquo;) == 0) {<br />
		strcpy(debugfs_mntpt, &laquo;/sys/kernel/debug/&raquo;);<br />
		return;<br />
	}</p>
<p>	/*<br />
	 * try the sane location<br />
	 */<br />
	if (valid_debugfs_mount(&raquo;/debug/&raquo;) == 0) {<br />
		strcpy(debugfs_mntpt, &laquo;/debug/&raquo;);<br />
		return;<br />
	}</p>
<p>	/*<br />
	 * give up and parse /proc/mounts<br />
	 */<br />
	file = fopen(&raquo;/proc/mounts&raquo;, &laquo;r&raquo;);<br />
	if (file == NULL)<br />
		return;</p>
<p>	while (fscanf(file, &laquo;%*s %&raquo;<br />
		      STR(MAXPATHLEN)<br />
		      &laquo;s %99s %*s %*d %*d\n&raquo;,<br />
		      debugfs, fs_type) == 2) {<br />
		if (strcmp(fs_type, &laquo;debugfs&raquo;) == 0)<br />
			break;<br />
	}<br />
	fclose(file);<br />
	if (strcmp(fs_type, &laquo;debugfs&raquo;) == 0) {<br />
		strncpy(debugfs_mntpt, debugfs, MAXPATHLEN);<br />
		debugfs_mntpt[MAXPATHLEN - 1] = &#8216;\0&#8242;;<br />
	}<br />
}</p>
<p>int main(int argc, const char **argv)<br />
{<br />
	const char *cmd;</p>
<p>	cmd = perf_extract_argv0_path(argv[0]);<br />
	if (!cmd)<br />
		cmd = &laquo;perf-help&raquo;;<br />
	/* get debugfs mount point from /proc/mounts */<br />
	get_debugfs_mntpt();<br />
	/*<br />
	 * &laquo;perf-xxxx&raquo; is the same as &laquo;perf xxxx&raquo;, but we obviously:<br />
	 *<br />
	 *  &#8211; cannot take flags in between the &laquo;perf&raquo; and the &laquo;xxxx&raquo;.<br />
	 *  &#8211; cannot execute it externally (since it would just do<br />
	 *    the same thing over again)<br />
	 *<br />
	 * So we just directly call the internal command handler, and<br />
	 * die if that one cannot handle it.<br />
	 */<br />
	if (!prefixcmp(cmd, &laquo;perf-&raquo;)) {<br />
		cmd += 5;<br />
		argv[0] = cmd;<br />
		handle_internal_command(argc, argv);<br />
		die(&raquo;cannot handle %s internally&raquo;, cmd);<br />
	}</p>
<p>	/* Look for flags.. */<br />
	argv++;<br />
	argc&#8211;;<br />
	handle_options(&#038;argv, &#038;argc, NULL);<br />
	commit_pager_choice();<br />
	set_debugfs_path();<br />
	if (argc > 0) {<br />
		if (!prefixcmp(argv[0], &laquo;&#8211;&raquo;))<br />
			argv[0] += 2;<br />
	} else {<br />
		/* The user didn&#8217;t specify a command; give them help */<br />
		printf(&raquo;\n usage: %s\n\n&raquo;, perf_usage_string);<br />
		list_common_cmds_help();<br />
		printf(&raquo;\n %s\n\n&raquo;, perf_more_info_string);<br />
		exit(1);<br />
	}<br />
	cmd = argv[0];</p>
<p>	/*<br />
	 * We use PATH to find perf commands, but we prepend some higher<br />
	 * precidence paths: the &laquo;&#8211;exec-path&raquo; option, the PERF_EXEC_PATH<br />
	 * environment, and the $(perfexecdir) from the Makefile at build<br />
	 * time.<br />
	 */<br />
	setup_path();</p>
<p>	while (1) {<br />
		static int done_help = 0;<br />
		static int was_alias = 0;</p>
<p>		was_alias = run_argv(&#038;argc, &#038;argv);<br />
		if (errno != ENOENT)<br />
			break;</p>
<p>		if (was_alias) {<br />
			fprintf(stderr, &laquo;Expansion of alias &#8216;%s&#8217; failed; &raquo;<br />
				&laquo;&#8216;%s&#8217; is not a perf-command\n&raquo;,<br />
				cmd, argv[0]);<br />
			exit(1);<br />
		}<br />
		if (!done_help) {<br />
			cmd = argv[0] = help_unknown_cmd(cmd);<br />
			done_help = 1;<br />
		} else<br />
			break;<br />
	}</p>
<p>	fprintf(stderr, &laquo;Failed to run command &#8216;%s&#8217;: %s\n&raquo;,<br />
		cmd, strerror(errno));</p>
<p>	return 1;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/perf-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Makefile</title>
		<link>http://lynyrd.ru/makefile-2</link>
		<comments>http://lynyrd.ru/makefile-2#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:40:36 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=619</guid>
		<description><![CDATA[# The default target of this Makefile is&#8230;
all::
# Define V=1 to have a more verbose compile.
#
# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()
# or vsnprintf() return -1 instead of number of characters which would
# have been written to the final string if enough space had been available.
#
# Define FREAD_READS_DIRECTORIES if your are ]]></description>
			<content:encoded><![CDATA[<p># The default target of this Makefile is&#8230;<br />
all::<span id="more-619"></span></p>
<p># Define V=1 to have a more verbose compile.<br />
#<br />
# Define SNPRINTF_RETURNS_BOGUS if your are on a system which snprintf()<br />
# or vsnprintf() return -1 instead of number of characters which would<br />
# have been written to the final string if enough space had been available.<br />
#<br />
# Define FREAD_READS_DIRECTORIES if your are on a system which succeeds<br />
# when attempting to read from an fopen&#8217;ed directory.<br />
#<br />
# Define NO_OPENSSL environment variable if you do not have OpenSSL.<br />
# This also implies MOZILLA_SHA1.<br />
#<br />
# Define CURLDIR=/foo/bar if your curl header and library files are in<br />
# /foo/bar/include and /foo/bar/lib directories.<br />
#<br />
# Define EXPATDIR=/foo/bar if your expat header and library files are in<br />
# /foo/bar/include and /foo/bar/lib directories.<br />
#<br />
# Define NO_D_INO_IN_DIRENT if you don&#8217;t have d_ino in your struct dirent.<br />
#<br />
# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks<br />
# d_type in struct dirent (latest Cygwin &#8212; will be fixed soonish).<br />
#<br />
# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)<br />
# do not support the &#8217;size specifiers&#8217; introduced by C99, namely ll, hh,<br />
# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).<br />
# some C compilers supported these specifiers prior to C99 as an extension.<br />
#<br />
# Define NO_STRCASESTR if you don&#8217;t have strcasestr.<br />
#<br />
# Define NO_MEMMEM if you don&#8217;t have memmem.<br />
#<br />
# Define NO_STRTOUMAX if you don&#8217;t have strtoumax in the C library.<br />
# If your compiler also does not support long long or does not have<br />
# strtoull, define NO_STRTOULL.<br />
#<br />
# Define NO_SETENV if you don&#8217;t have setenv in the C library.<br />
#<br />
# Define NO_UNSETENV if you don&#8217;t have unsetenv in the C library.<br />
#<br />
# Define NO_MKDTEMP if you don&#8217;t have mkdtemp in the C library.<br />
#<br />
# Define NO_SYS_SELECT_H if you don&#8217;t have sys/select.h.<br />
#<br />
# Define NO_SYMLINK_HEAD if you never want .perf/HEAD to be a symbolic link.<br />
# Enable it on Windows.  By default, symrefs are still used.<br />
#<br />
# Define NO_SVN_TESTS if you want to skip time-consuming SVN interoperability<br />
# tests.  These tests take up a significant amount of the total test time<br />
# but are not needed unless you plan to talk to SVN repos.<br />
#<br />
# Define NO_FINK if you are building on Darwin/Mac OS X, have Fink<br />
# installed in /sw, but don&#8217;t want PERF to link against any libraries<br />
# installed there.  If defined you may specify your own (or Fink&#8217;s)<br />
# include directories and library directories by defining CFLAGS<br />
# and LDFLAGS appropriately.<br />
#<br />
# Define NO_DARWIN_PORTS if you are building on Darwin/Mac OS X,<br />
# have DarwinPorts installed in /opt/local, but don&#8217;t want PERF to<br />
# link against any libraries installed there.  If defined you may<br />
# specify your own (or DarwinPort&#8217;s) include directories and<br />
# library directories by defining CFLAGS and LDFLAGS appropriately.<br />
#<br />
# Define PPC_SHA1 environment variable when running make to make use of<br />
# a bundled SHA1 routine optimized for PowerPC.<br />
#<br />
# Define ARM_SHA1 environment variable when running make to make use of<br />
# a bundled SHA1 routine optimized for ARM.<br />
#<br />
# Define MOZILLA_SHA1 environment variable when running make to make use of<br />
# a bundled SHA1 routine coming from Mozilla. It is GPL&#8217;d and should be fast<br />
# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default<br />
# choice) has very fast version optimized for i586.<br />
#<br />
# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).<br />
#<br />
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).<br />
#<br />
# Define NEEDS_SOCKET if linking with libc is not enough (SunOS,<br />
# Patrick Mauritz).<br />
#<br />
# Define NO_MMAP if you want to avoid mmap.<br />
#<br />
# Define NO_PTHREADS if you do not have or do not want to use Pthreads.<br />
#<br />
# Define NO_PREAD if you have a problem with pread() system call (e.g.<br />
# cygwin.dll before v1.5.22).<br />
#<br />
# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is<br />
# generally faster on your platform than accessing the working directory.<br />
#<br />
# Define NO_TRUSTABLE_FILEMODE if your filesystem may claim to support<br />
# the executable mode bit, but doesn&#8217;t really do so.<br />
#<br />
# Define NO_IPV6 if you lack IPv6 support and getaddrinfo().<br />
#<br />
# Define NO_SOCKADDR_STORAGE if your platform does not have struct<br />
# sockaddr_storage.<br />
#<br />
# Define NO_ICONV if your libc does not properly support iconv.<br />
#<br />
# Define OLD_ICONV if your library has an old iconv(), where the second<br />
# (input buffer pointer) parameter is declared with type (const char **).<br />
#<br />
# Define NO_DEFLATE_BOUND if your zlib does not have deflateBound.<br />
#<br />
# Define NO_R_TO_GCC_LINKER if your gcc does not like &laquo;-R/path/lib&raquo;<br />
# that tells runtime paths to dynamic libraries;<br />
# &laquo;-Wl,-rpath=/path/lib&raquo; is used instead.<br />
#<br />
# Define USE_NSEC below if you want perf to care about sub-second file mtimes<br />
# and ctimes. Note that you need recent glibc (at least 2.2.4) for this, and<br />
# it will BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely<br />
# randomly break unless your underlying filesystem supports those sub-second<br />
# times (my ext3 doesn&#8217;t).<br />
#<br />
# Define USE_ST_TIMESPEC if your &laquo;struct stat&raquo; uses &laquo;st_ctimespec&raquo; instead of<br />
# &laquo;st_ctim&raquo;<br />
#<br />
# Define NO_NSEC if your &laquo;struct stat&raquo; does not have &laquo;st_ctim.tv_nsec&raquo;<br />
# available.  This automatically turns USE_NSEC off.<br />
#<br />
# Define USE_STDEV below if you want perf to care about the underlying device<br />
# change being considered an inode change from the update-index perspective.<br />
#<br />
# Define NO_ST_BLOCKS_IN_STRUCT_STAT if your platform does not have st_blocks<br />
# field that counts the on-disk footprint in 512-byte blocks.<br />
#<br />
# Define ASCIIDOC8 if you want to format documentation with AsciiDoc 8<br />
#<br />
# Define DOCBOOK_XSL_172 if you want to format man pages with DocBook XSL v1.72.<br />
#<br />
# Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl&#8217;s<br />
# MakeMaker (e.g. using ActiveState under Cygwin).<br />
#<br />
# Define NO_PERL if you do not want Perl scripts or libraries at all.<br />
#<br />
# Define INTERNAL_QSORT to use Git&#8217;s implementation of qsort(), which<br />
# is a simplified version of the merge sort used in glibc. This is<br />
# recommended if Git triggers O(n^2) behavior in your platform&#8217;s qsort().<br />
#<br />
# Define NO_EXTERNAL_GREP if you don&#8217;t want &laquo;perf grep&raquo; to ever call<br />
# your external grep (e.g., if your system lacks grep, if its grep is<br />
# broken, or spawning external process is slower than built-in grep perf has).</p>
<p>PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE<br />
	@$(SHELL_PATH) util/PERF-VERSION-GEN<br />
-include PERF-VERSION-FILE</p>
<p>uname_S := $(shell sh -c &#8216;uname -s 2>/dev/null || echo not&#8217;)<br />
uname_M := $(shell sh -c &#8216;uname -m 2>/dev/null || echo not&#8217;)<br />
uname_O := $(shell sh -c &#8216;uname -o 2>/dev/null || echo not&#8217;)<br />
uname_R := $(shell sh -c &#8216;uname -r 2>/dev/null || echo not&#8217;)<br />
uname_P := $(shell sh -c &#8216;uname -p 2>/dev/null || echo not&#8217;)<br />
uname_V := $(shell sh -c &#8216;uname -v 2>/dev/null || echo not&#8217;)</p>
<p>#<br />
# Add -m32 for cross-builds:<br />
#<br />
ifdef NO_64BIT<br />
  MBITS := -m32<br />
else<br />
  #<br />
  # If we&#8217;re on a 64-bit kernel, use -m64:<br />
  #<br />
  ifneq ($(patsubst %64,%,$(uname_M)),$(uname_M))<br />
    MBITS := -m64<br />
  endif<br />
endif</p>
<p># CFLAGS and LDFLAGS are for the users to override from the command line.</p>
<p>#<br />
# Include saner warnings here, which can catch bugs:<br />
#</p>
<p>EXTRA_WARNINGS := -Wformat<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-security<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wformat-y2k<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wshadow<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Winit-self<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wpacked<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wredundant-decls<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstack-protector<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-aliasing=3<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-default<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wswitch-enum<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wno-system-headers<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wundef<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wvolatile-register-var<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wwrite-strings<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wbad-function-cast<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-declarations<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wmissing-prototypes<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wnested-externs<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wold-style-definition<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wstrict-prototypes<br />
EXTRA_WARNINGS := $(EXTRA_WARNINGS) -Wdeclaration-after-statement</p>
<p>CFLAGS = $(MBITS) -ggdb3 -Wall -Wextra -std=gnu99 -Werror -O6 -fstack-protector-all -D_FORTIFY_SOURCE=2 $(EXTRA_WARNINGS)<br />
LDFLAGS = -lpthread -lrt -lelf -lm<br />
ALL_CFLAGS = $(CFLAGS)<br />
ALL_LDFLAGS = $(LDFLAGS)<br />
STRIP ?= strip</p>
<p># Among the variables below, these:<br />
#   perfexecdir<br />
#   template_dir<br />
#   mandir<br />
#   infodir<br />
#   htmldir<br />
#   ETC_PERFCONFIG (but not sysconfdir)<br />
# can be specified as a relative path some/where/else;<br />
# this is interpreted as relative to $(prefix) and &laquo;perf&raquo; at<br />
# runtime figures out where they are based on the path to the executable.<br />
# This can help installing the suite in a relocatable way.</p>
<p>prefix = $(HOME)<br />
bindir_relative = bin<br />
bindir = $(prefix)/$(bindir_relative)<br />
mandir = share/man<br />
infodir = share/info<br />
perfexecdir = libexec/perf-core<br />
sharedir = $(prefix)/share<br />
template_dir = share/perf-core/templates<br />
htmldir = share/doc/perf-doc<br />
ifeq ($(prefix),/usr)<br />
sysconfdir = /etc<br />
ETC_PERFCONFIG = $(sysconfdir)/perfconfig<br />
else<br />
sysconfdir = $(prefix)/etc<br />
ETC_PERFCONFIG = etc/perfconfig<br />
endif<br />
lib = lib<br />
# DESTDIR=</p>
<p>export prefix bindir sharedir sysconfdir</p>
<p>CC = gcc<br />
AR = ar<br />
RM = rm -f<br />
TAR = tar<br />
FIND = find<br />
INSTALL = install<br />
RPMBUILD = rpmbuild<br />
PTHREAD_LIBS = -lpthread</p>
<p># sparse is architecture-neutral, which means that we need to tell it<br />
# explicitly what architecture to check for. Fix this up for yours..<br />
SPARSE_FLAGS = -D__BIG_ENDIAN__ -D__powerpc__</p>
<p>### &#8212; END CONFIGURATION SECTION &#8212;</p>
<p># Those must not be GNU-specific; they are shared with perl/ which may<br />
# be built by a different compiler. (Note that this is an artifact now<br />
# but it still might be nice to keep that distinction.)<br />
BASIC_CFLAGS = -Iutil/include<br />
BASIC_LDFLAGS =</p>
<p># Guard against environment variables<br />
BUILTIN_OBJS =<br />
BUILT_INS =<br />
COMPAT_CFLAGS =<br />
COMPAT_OBJS =<br />
LIB_H =<br />
LIB_OBJS =<br />
SCRIPT_PERL =<br />
SCRIPT_SH =<br />
TEST_PROGRAMS =</p>
<p>#<br />
# No scripts right now:<br />
#</p>
<p># SCRIPT_SH += perf-am.sh</p>
<p>#<br />
# No Perl scripts right now:<br />
#</p>
<p># SCRIPT_PERL += perf-add&#8211;interactive.perl</p>
<p>SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \<br />
	  $(patsubst %.perl,%,$(SCRIPT_PERL))</p>
<p># Empty&#8230;<br />
EXTRA_PROGRAMS =</p>
<p># &#8230; and all the rest that could be moved out of bindir to perfexecdir<br />
PROGRAMS += $(EXTRA_PROGRAMS)</p>
<p>#<br />
# Single &#8216;perf&#8217; binary right now:<br />
#<br />
PROGRAMS += perf</p>
<p># List built-in command $C whose implementation cmd_$C() is not in<br />
# builtin-$C.o but is linked in as part of some other command.<br />
#<br />
# None right now:<br />
#<br />
# BUILT_INS += perf-init $X</p>
<p># what &#8216;all&#8217; will build and &#8216;install&#8217; will install, in perfexecdir<br />
ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)</p>
<p># what &#8216;all&#8217; will build but not install in perfexecdir<br />
OTHER_PROGRAMS = perf$X</p>
<p># Set paths to tools early so that they can be used for version tests.<br />
ifndef SHELL_PATH<br />
	SHELL_PATH = /bin/sh<br />
endif<br />
ifndef PERL_PATH<br />
	PERL_PATH = /usr/bin/perl<br />
endif</p>
<p>export PERL_PATH</p>
<p>LIB_FILE=libperf.a</p>
<p>LIB_H += ../../include/linux/perf_event.h<br />
LIB_H += ../../include/linux/rbtree.h<br />
LIB_H += ../../include/linux/list.h<br />
LIB_H += util/include/linux/list.h<br />
LIB_H += perf.h<br />
LIB_H += util/types.h<br />
LIB_H += util/levenshtein.h<br />
LIB_H += util/parse-options.h<br />
LIB_H += util/parse-events.h<br />
LIB_H += util/quote.h<br />
LIB_H += util/util.h<br />
LIB_H += util/help.h<br />
LIB_H += util/strbuf.h<br />
LIB_H += util/string.h<br />
LIB_H += util/strlist.h<br />
LIB_H += util/run-command.h<br />
LIB_H += util/sigchain.h<br />
LIB_H += util/symbol.h<br />
LIB_H += util/module.h<br />
LIB_H += util/color.h<br />
LIB_H += util/values.h</p>
<p>LIB_OBJS += util/abspath.o<br />
LIB_OBJS += util/alias.o<br />
LIB_OBJS += util/config.o<br />
LIB_OBJS += util/ctype.o<br />
LIB_OBJS += util/environment.o<br />
LIB_OBJS += util/exec_cmd.o<br />
LIB_OBJS += util/help.o<br />
LIB_OBJS += util/levenshtein.o<br />
LIB_OBJS += util/parse-options.o<br />
LIB_OBJS += util/parse-events.o<br />
LIB_OBJS += util/path.o<br />
LIB_OBJS += util/rbtree.o<br />
LIB_OBJS += util/run-command.o<br />
LIB_OBJS += util/quote.o<br />
LIB_OBJS += util/strbuf.o<br />
LIB_OBJS += util/string.o<br />
LIB_OBJS += util/strlist.o<br />
LIB_OBJS += util/usage.o<br />
LIB_OBJS += util/wrapper.o<br />
LIB_OBJS += util/sigchain.o<br />
LIB_OBJS += util/symbol.o<br />
LIB_OBJS += util/module.o<br />
LIB_OBJS += util/color.o<br />
LIB_OBJS += util/pager.o<br />
LIB_OBJS += util/header.o<br />
LIB_OBJS += util/callchain.o<br />
LIB_OBJS += util/values.o<br />
LIB_OBJS += util/debug.o<br />
LIB_OBJS += util/map.o<br />
LIB_OBJS += util/thread.o<br />
LIB_OBJS += util/trace-event-parse.o<br />
LIB_OBJS += util/trace-event-read.o<br />
LIB_OBJS += util/trace-event-info.o<br />
LIB_OBJS += util/svghelper.o</p>
<p>BUILTIN_OBJS += builtin-annotate.o<br />
BUILTIN_OBJS += builtin-help.o<br />
BUILTIN_OBJS += builtin-sched.o<br />
BUILTIN_OBJS += builtin-list.o<br />
BUILTIN_OBJS += builtin-record.o<br />
BUILTIN_OBJS += builtin-report.o<br />
BUILTIN_OBJS += builtin-stat.o<br />
BUILTIN_OBJS += builtin-timechart.o<br />
BUILTIN_OBJS += builtin-top.o<br />
BUILTIN_OBJS += builtin-trace.o</p>
<p>PERFLIBS = $(LIB_FILE)</p>
<p>#<br />
# Platform specific tweaks<br />
#</p>
<p># We choose to avoid &laquo;if .. else if .. else .. endif endif&raquo;<br />
# because maintaining the nesting to match is a pain.  If<br />
# we had &laquo;elif&raquo; things would have been much nicer&#8230;</p>
<p>-include config.mak.autogen<br />
-include config.mak</p>
<p>ifeq ($(uname_S),Darwin)<br />
	ifndef NO_FINK<br />
		ifeq ($(shell test -d /sw/lib &#038;&#038; echo y),y)<br />
			BASIC_CFLAGS += -I/sw/include<br />
			BASIC_LDFLAGS += -L/sw/lib<br />
		endif<br />
	endif<br />
	ifndef NO_DARWIN_PORTS<br />
		ifeq ($(shell test -d /opt/local/lib &#038;&#038; echo y),y)<br />
			BASIC_CFLAGS += -I/opt/local/include<br />
			BASIC_LDFLAGS += -L/opt/local/lib<br />
		endif<br />
	endif<br />
	PTHREAD_LIBS =<br />
endif</p>
<p>ifeq ($(shell sh -c &laquo;(echo &#8216;\#include
<libelf.h>&#8216;; echo &#8216;int main(void) { Elf * elf = elf_begin(0, ELF_C_READ, 0); return (long)elf; }&#8217;) | $(CC) -x c &#8211; $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&#038;1 &#038;&#038; echo y&raquo;), y)<br />
	ifneq ($(shell sh -c &laquo;(echo &#8216;\#include
<libelf.h>&#8216;; echo &#8216;int main(void) { Elf * elf = elf_begin(0, ELF_C_READ_MMAP, 0); return (long)elf; }&#8217;) | $(CC) -x c &#8211; $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -o /dev/null $(ALL_LDFLAGS) > /dev/null 2>&#038;1 &#038;&#038; echo y&raquo;), y)<br />
		BASIC_CFLAGS += -DLIBELF_NO_MMAP<br />
	endif<br />
else<br />
	msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel and glibc-dev[el]);<br />
endif</p>
<p>ifdef NO_DEMANGLE<br />
	BASIC_CFLAGS += -DNO_DEMANGLE<br />
else<br />
	has_bfd := $(shell sh -c &laquo;(echo &#8216;\#include <bfd.h>&#8216;; echo &#8216;int main(void) { bfd_demangle(0, 0, 0); return 0; }&#8217;) | $(CC) -x c &#8211; $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd > /dev/null 2>&#038;1 &#038;&#038; echo y&raquo;)</p>
<p>	ifeq ($(has_bfd),y)<br />
		EXTLIBS += -lbfd<br />
	else<br />
		has_bfd_iberty := $(shell sh -c &laquo;(echo &#8216;\#include <bfd.h>&#8216;; echo &#8216;int main(void) { bfd_demangle(0, 0, 0); return 0; }&#8217;) | $(CC) -x c &#8211; $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty > /dev/null 2>&#038;1 &#038;&#038; echo y&raquo;)<br />
		ifeq ($(has_bfd_iberty),y)<br />
			EXTLIBS += -lbfd -liberty<br />
		else<br />
			has_bfd_iberty_z := $(shell sh -c &laquo;(echo &#8216;\#include <bfd.h>&#8216;; echo &#8216;int main(void) { bfd_demangle(0, 0, 0); return 0; }&#8217;) | $(CC) -x c &#8211; $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -lbfd -liberty -lz > /dev/null 2>&#038;1 &#038;&#038; echo y&raquo;)<br />
			ifeq ($(has_bfd_iberty_z),y)<br />
				EXTLIBS += -lbfd -liberty -lz<br />
			else<br />
				has_cplus_demangle := $(shell sh -c &laquo;(echo &#8216;extern char *cplus_demangle(const char *, int);&#8217;; echo &#8216;int main(void) { cplus_demangle(0, 0); return 0; }&#8217;) | $(CC) -x c &#8211; $(ALL_CFLAGS) -o /dev/null $(ALL_LDFLAGS) -liberty > /dev/null 2>&#038;1 &#038;&#038; echo y&raquo;)<br />
				ifeq ($(has_cplus_demangle),y)<br />
					EXTLIBS += -liberty<br />
					BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE<br />
				else<br />
					msg := $(warning No bfd.h/libbfd found, install binutils-dev[el] to gain symbol demangling)<br />
					BASIC_CFLAGS += -DNO_DEMANGLE<br />
				endif<br />
			endif<br />
		endif<br />
	endif<br />
endif</p>
<p>ifndef CC_LD_DYNPATH<br />
	ifdef NO_R_TO_GCC_LINKER<br />
		# Some gcc does not accept and pass -R to the linker to specify<br />
		# the runtime dynamic library path.<br />
		CC_LD_DYNPATH = -Wl,-rpath,<br />
	else<br />
		CC_LD_DYNPATH = -R<br />
	endif<br />
endif</p>
<p>ifdef NEEDS_SOCKET<br />
	EXTLIBS += -lsocket<br />
endif<br />
ifdef NEEDS_NSL<br />
	EXTLIBS += -lnsl<br />
endif<br />
ifdef NO_D_TYPE_IN_DIRENT<br />
	BASIC_CFLAGS += -DNO_D_TYPE_IN_DIRENT<br />
endif<br />
ifdef NO_D_INO_IN_DIRENT<br />
	BASIC_CFLAGS += -DNO_D_INO_IN_DIRENT<br />
endif<br />
ifdef NO_ST_BLOCKS_IN_STRUCT_STAT<br />
	BASIC_CFLAGS += -DNO_ST_BLOCKS_IN_STRUCT_STAT<br />
endif<br />
ifdef USE_NSEC<br />
	BASIC_CFLAGS += -DUSE_NSEC<br />
endif<br />
ifdef USE_ST_TIMESPEC<br />
	BASIC_CFLAGS += -DUSE_ST_TIMESPEC<br />
endif<br />
ifdef NO_NSEC<br />
	BASIC_CFLAGS += -DNO_NSEC<br />
endif<br />
ifdef NO_C99_FORMAT<br />
	BASIC_CFLAGS += -DNO_C99_FORMAT<br />
endif<br />
ifdef SNPRINTF_RETURNS_BOGUS<br />
	COMPAT_CFLAGS += -DSNPRINTF_RETURNS_BOGUS<br />
	COMPAT_OBJS += compat/snprintf.o<br />
endif<br />
ifdef FREAD_READS_DIRECTORIES<br />
	COMPAT_CFLAGS += -DFREAD_READS_DIRECTORIES<br />
	COMPAT_OBJS += compat/fopen.o<br />
endif<br />
ifdef NO_SYMLINK_HEAD<br />
	BASIC_CFLAGS += -DNO_SYMLINK_HEAD<br />
endif<br />
ifdef NO_STRCASESTR<br />
	COMPAT_CFLAGS += -DNO_STRCASESTR<br />
	COMPAT_OBJS += compat/strcasestr.o<br />
endif<br />
ifdef NO_STRTOUMAX<br />
	COMPAT_CFLAGS += -DNO_STRTOUMAX<br />
	COMPAT_OBJS += compat/strtoumax.o<br />
endif<br />
ifdef NO_STRTOULL<br />
	COMPAT_CFLAGS += -DNO_STRTOULL<br />
endif<br />
ifdef NO_SETENV<br />
	COMPAT_CFLAGS += -DNO_SETENV<br />
	COMPAT_OBJS += compat/setenv.o<br />
endif<br />
ifdef NO_MKDTEMP<br />
	COMPAT_CFLAGS += -DNO_MKDTEMP<br />
	COMPAT_OBJS += compat/mkdtemp.o<br />
endif<br />
ifdef NO_UNSETENV<br />
	COMPAT_CFLAGS += -DNO_UNSETENV<br />
	COMPAT_OBJS += compat/unsetenv.o<br />
endif<br />
ifdef NO_SYS_SELECT_H<br />
	BASIC_CFLAGS += -DNO_SYS_SELECT_H<br />
endif<br />
ifdef NO_MMAP<br />
	COMPAT_CFLAGS += -DNO_MMAP<br />
	COMPAT_OBJS += compat/mmap.o<br />
else<br />
	ifdef USE_WIN32_MMAP<br />
		COMPAT_CFLAGS += -DUSE_WIN32_MMAP<br />
		COMPAT_OBJS += compat/win32mmap.o<br />
	endif<br />
endif<br />
ifdef NO_PREAD<br />
	COMPAT_CFLAGS += -DNO_PREAD<br />
	COMPAT_OBJS += compat/pread.o<br />
endif<br />
ifdef NO_FAST_WORKING_DIRECTORY<br />
	BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY<br />
endif<br />
ifdef NO_TRUSTABLE_FILEMODE<br />
	BASIC_CFLAGS += -DNO_TRUSTABLE_FILEMODE<br />
endif<br />
ifdef NO_IPV6<br />
	BASIC_CFLAGS += -DNO_IPV6<br />
endif<br />
ifdef NO_UINTMAX_T<br />
	BASIC_CFLAGS += -Duintmax_t=uint32_t<br />
endif<br />
ifdef NO_SOCKADDR_STORAGE<br />
ifdef NO_IPV6<br />
	BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in<br />
else<br />
	BASIC_CFLAGS += -Dsockaddr_storage=sockaddr_in6<br />
endif<br />
endif<br />
ifdef NO_INET_NTOP<br />
	LIB_OBJS += compat/inet_ntop.o<br />
endif<br />
ifdef NO_INET_PTON<br />
	LIB_OBJS += compat/inet_pton.o<br />
endif</p>
<p>ifdef NO_ICONV<br />
	BASIC_CFLAGS += -DNO_ICONV<br />
endif</p>
<p>ifdef OLD_ICONV<br />
	BASIC_CFLAGS += -DOLD_ICONV<br />
endif</p>
<p>ifdef NO_DEFLATE_BOUND<br />
	BASIC_CFLAGS += -DNO_DEFLATE_BOUND<br />
endif</p>
<p>ifdef PPC_SHA1<br />
	SHA1_HEADER = &laquo;ppc/sha1.h&raquo;<br />
	LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o<br />
else<br />
ifdef ARM_SHA1<br />
	SHA1_HEADER = &laquo;arm/sha1.h&raquo;<br />
	LIB_OBJS += arm/sha1.o arm/sha1_arm.o<br />
else<br />
ifdef MOZILLA_SHA1<br />
	SHA1_HEADER = &laquo;mozilla-sha1/sha1.h&raquo;<br />
	LIB_OBJS += mozilla-sha1/sha1.o<br />
else<br />
	SHA1_HEADER = <openssl/sha.h><br />
	EXTLIBS += $(LIB_4_CRYPTO)<br />
endif<br />
endif<br />
endif<br />
ifdef NO_PERL_MAKEMAKER<br />
	export NO_PERL_MAKEMAKER<br />
endif<br />
ifdef NO_HSTRERROR<br />
	COMPAT_CFLAGS += -DNO_HSTRERROR<br />
	COMPAT_OBJS += compat/hstrerror.o<br />
endif<br />
ifdef NO_MEMMEM<br />
	COMPAT_CFLAGS += -DNO_MEMMEM<br />
	COMPAT_OBJS += compat/memmem.o<br />
endif<br />
ifdef INTERNAL_QSORT<br />
	COMPAT_CFLAGS += -DINTERNAL_QSORT<br />
	COMPAT_OBJS += compat/qsort.o<br />
endif<br />
ifdef RUNTIME_PREFIX<br />
	COMPAT_CFLAGS += -DRUNTIME_PREFIX<br />
endif</p>
<p>ifdef DIR_HAS_BSD_GROUP_SEMANTICS<br />
	COMPAT_CFLAGS += -DDIR_HAS_BSD_GROUP_SEMANTICS<br />
endif<br />
ifdef NO_EXTERNAL_GREP<br />
	BASIC_CFLAGS += -DNO_EXTERNAL_GREP<br />
endif</p>
<p>ifeq ($(PERL_PATH),)<br />
NO_PERL=NoThanks<br />
endif</p>
<p>QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir<br />
QUIET_SUBDIR1  =</p>
<p>ifneq ($(findstring $(MAKEFLAGS),w),w)<br />
PRINT_DIR = &#8211;no-print-directory<br />
else # &laquo;make -w&raquo;<br />
NO_SUBDIR = :<br />
endif</p>
<p>ifneq ($(findstring $(MAKEFLAGS),s),s)<br />
ifndef V<br />
	QUIET_CC       = @echo &#8216;   &#8216; CC $@;<br />
	QUIET_AR       = @echo &#8216;   &#8216; AR $@;<br />
	QUIET_LINK     = @echo &#8216;   &#8216; LINK $@;<br />
	QUIET_BUILT_IN = @echo &#8216;   &#8216; BUILTIN $@;<br />
	QUIET_GEN      = @echo &#8216;   &#8216; GEN $@;<br />
	QUIET_SUBDIR0  = +@subdir=<br />
	QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo &#8216;   &#8216; SUBDIR $$subdir; \<br />
			 $(MAKE) $(PRINT_DIR) -C $$subdir<br />
	export V<br />
	export QUIET_GEN<br />
	export QUIET_BUILT_IN<br />
endif<br />
endif</p>
<p>ifdef ASCIIDOC8<br />
	export ASCIIDOC8<br />
endif</p>
<p># Shell quote (do not use $(call) to accommodate ancient setups);</p>
<p>SHA1_HEADER_SQ = $(subst &#8216;,&#8217;\&raquo;,$(SHA1_HEADER))<br />
ETC_PERFCONFIG_SQ = $(subst &#8216;,&#8217;\&raquo;,$(ETC_PERFCONFIG))</p>
<p>DESTDIR_SQ = $(subst &#8216;,&#8217;\&raquo;,$(DESTDIR))<br />
bindir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(bindir))<br />
bindir_relative_SQ = $(subst &#8216;,&#8217;\&raquo;,$(bindir_relative))<br />
mandir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(mandir))<br />
infodir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(infodir))<br />
perfexecdir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(perfexecdir))<br />
template_dir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(template_dir))<br />
htmldir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(htmldir))<br />
prefix_SQ = $(subst &#8216;,&#8217;\&raquo;,$(prefix))</p>
<p>SHELL_PATH_SQ = $(subst &#8216;,&#8217;\&raquo;,$(SHELL_PATH))<br />
PERL_PATH_SQ = $(subst &#8216;,&#8217;\&raquo;,$(PERL_PATH))</p>
<p>LIBS = $(PERFLIBS) $(EXTLIBS)</p>
<p>BASIC_CFLAGS += -DSHA1_HEADER=&#8217;$(SHA1_HEADER_SQ)&#8217; \<br />
	$(COMPAT_CFLAGS)<br />
LIB_OBJS += $(COMPAT_OBJS)</p>
<p>ALL_CFLAGS += $(BASIC_CFLAGS)<br />
ALL_LDFLAGS += $(BASIC_LDFLAGS)</p>
<p>export TAR INSTALL DESTDIR SHELL_PATH</p>
<p>### Build rules</p>
<p>SHELL = $(SHELL_PATH)</p>
<p>all:: shell_compatibility_test $(ALL_PROGRAMS) $(BUILT_INS) $(OTHER_PROGRAMS) PERF-BUILD-OPTIONS<br />
ifneq (,$X)<br />
	$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), test &#8216;$p&#8217; -ef &#8216;$p$X&#8217; || $(RM) &#8216;$p&#8217;;)<br />
endif</p>
<p>all::</p>
<p>please_set_SHELL_PATH_to_a_more_modern_shell:<br />
	@$$(:)</p>
<p>shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell</p>
<p>strip: $(PROGRAMS) perf$X<br />
	$(STRIP) $(STRIP_OPTS) $(PROGRAMS) perf$X</p>
<p>perf.o: perf.c common-cmds.h PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -DPERF_VERSION=&#8217;&raquo;$(PERF_VERSION)&raquo;&#8216; \<br />
		&#8216;-DPERF_HTML_PATH=&raquo;$(htmldir_SQ)&raquo;&#8216; \<br />
		$(ALL_CFLAGS) -c $(filter %.c,$^)</p>
<p>perf$X: perf.o $(BUILTIN_OBJS) $(PERFLIBS)<br />
	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ perf.o \<br />
		$(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)</p>
<p>builtin-help.o: builtin-help.c common-cmds.h PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \<br />
		&#8216;-DPERF_HTML_PATH=&raquo;$(htmldir_SQ)&raquo;&#8216; \<br />
		&#8216;-DPERF_MAN_PATH=&raquo;$(mandir_SQ)&raquo;&#8216; \<br />
		&#8216;-DPERF_INFO_PATH=&raquo;$(infodir_SQ)&raquo;&#8216; $<</p>
<p>builtin-timechart.o: builtin-timechart.c common-cmds.h PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \<br />
		'-DPERF_HTML_PATH="$(htmldir_SQ)"' \<br />
		'-DPERF_MAN_PATH="$(mandir_SQ)"' \<br />
		'-DPERF_INFO_PATH="$(infodir_SQ)"' $<</p>
<p>$(BUILT_INS): perf$X<br />
	$(QUIET_BUILT_IN)$(RM) $@ &#038;&#038; \<br />
	ln perf$X $@ 2>/dev/null || \<br />
	ln -s perf$X $@ 2>/dev/null || \<br />
	cp perf$X $@</p>
<p>common-cmds.h: util/generate-cmdlist.sh command-list.txt</p>
<p>common-cmds.h: $(wildcard Documentation/perf-*.txt)<br />
	$(QUIET_GEN). util/generate-cmdlist.sh > $@+ &#038;&#038; mv $@+ $@</p>
<p>$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh<br />
	$(QUIET_GEN)$(RM) $@ $@+ &#038;&#038; \<br />
	sed -e &#8216;1s|#!.*/sh|#!$(SHELL_PATH_SQ)|&#8217; \<br />
	    -e &#8217;s|@SHELL_PATH@|$(SHELL_PATH_SQ)|&#8217; \<br />
	    -e &#8217;s|@@PERL@@|$(PERL_PATH_SQ)|g&#8217; \<br />
	    -e &#8217;s/@@PERF_VERSION@@/$(PERF_VERSION)/g&#8217; \<br />
	    -e &#8217;s/@@NO_CURL@@/$(NO_CURL)/g&#8217; \<br />
	    $@.sh >$@+ &#038;&#038; \<br />
	chmod +x $@+ &#038;&#038; \<br />
	mv $@+ $@</p>
<p>configure: configure.ac<br />
	$(QUIET_GEN)$(RM) $@ $<+ &#038;&#038; \<br />
	sed -e 's/@@PERF_VERSION@@/$(PERF_VERSION)/g' \<br />
	    $< > $<+ &#038;&#038; \<br />
	autoconf -o $@ $<+ &#038;&#038; \<br />
	$(RM) $<+</p>
<p># These can record PERF_VERSION<br />
perf.o perf.spec \<br />
	$(patsubst %.sh,%,$(SCRIPT_SH)) \<br />
	$(patsubst %.perl,%,$(SCRIPT_PERL)) \<br />
	: PERF-VERSION-FILE</p>
<p>%.o: %.c PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<<br />
%.s: %.c PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<<br />
%.o: %.S<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<</p>
<p>util/exec_cmd.o: util/exec_cmd.c PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \<br />
		'-DPERF_EXEC_PATH="$(perfexecdir_SQ)"' \<br />
		'-DBINDIR="$(bindir_relative_SQ)"' \<br />
		'-DPREFIX="$(prefix_SQ)"' \<br />
		$<</p>
<p>builtin-init-db.o: builtin-init-db.c PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_PERF_TEMPLATE_DIR='"$(template_dir_SQ)"' $<</p>
<p>util/config.o: util/config.c PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<</p>
<p>util/rbtree.o: ../../lib/rbtree.c PERF-CFLAGS<br />
	$(QUIET_CC)$(CC) -o util/rbtree.o -c $(ALL_CFLAGS) -DETC_PERFCONFIG='"$(ETC_PERFCONFIG_SQ)"' $<</p>
<p>perf-%$X: %.o $(PERFLIBS)<br />
	$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)</p>
<p>$(LIB_OBJS) $(BUILTIN_OBJS): $(LIB_H)<br />
$(patsubst perf-%$X,%.o,$(PROGRAMS)): $(LIB_H) $(wildcard */*.h)<br />
builtin-revert.o wt-status.o: wt-status.h</p>
<p>$(LIB_FILE): $(LIB_OBJS)<br />
	$(QUIET_AR)$(RM) $@ &#038;&#038; $(AR) rcs $@ $(LIB_OBJS)</p>
<p>doc:<br />
	$(MAKE) -C Documentation all</p>
<p>man:<br />
	$(MAKE) -C Documentation man</p>
<p>html:<br />
	$(MAKE) -C Documentation html</p>
<p>info:<br />
	$(MAKE) -C Documentation info</p>
<p>pdf:<br />
	$(MAKE) -C Documentation pdf</p>
<p>TAGS:<br />
	$(RM) TAGS<br />
	$(FIND) . -name '*.[hcS]' -print | xargs etags -a</p>
<p>tags:<br />
	$(RM) tags<br />
	$(FIND) . -name '*.[hcS]' -print | xargs ctags -a</p>
<p>cscope:<br />
	$(RM) cscope*<br />
	$(FIND) . -name '*.[hcS]' -print | xargs cscope -b</p>
<p>### Detect prefix changes<br />
TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\<br />
             $(bindir_SQ):$(perfexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)</p>
<p>PERF-CFLAGS: .FORCE-PERF-CFLAGS<br />
	@FLAGS='$(TRACK_CFLAGS)'; \<br />
	    if test x"$$FLAGS" != x"`cat PERF-CFLAGS 2>/dev/null`&raquo; ; then \<br />
		echo 1>&#038;2 &raquo;    * new build flags or prefix&raquo;; \<br />
		echo &laquo;$$FLAGS&raquo; >PERF-CFLAGS; \<br />
            fi</p>
<p># We need to apply sq twice, once to protect from the shell<br />
# that runs PERF-BUILD-OPTIONS, and then again to protect it<br />
# and the first level quoting from the shell that runs &laquo;echo&raquo;.<br />
PERF-BUILD-OPTIONS: .FORCE-PERF-BUILD-OPTIONS<br />
	@echo SHELL_PATH=\&raquo;$(subst &#8216;,&#8217;\&raquo;,$(SHELL_PATH_SQ))&#8217;\&#8217; >$@<br />
	@echo TAR=\&raquo;$(subst &#8216;,&#8217;\&raquo;,$(subst &#8216;,&#8217;\&raquo;,$(TAR)))&#8217;\&#8217; >>$@<br />
	@echo NO_CURL=\&raquo;$(subst &#8216;,&#8217;\&raquo;,$(subst &#8216;,&#8217;\&raquo;,$(NO_CURL)))&#8217;\&#8217; >>$@<br />
	@echo NO_PERL=\&raquo;$(subst &#8216;,&#8217;\&raquo;,$(subst &#8216;,&#8217;\&raquo;,$(NO_PERL)))&#8217;\&#8217; >>$@</p>
<p>### Testing rules</p>
<p>#<br />
# None right now:<br />
#<br />
# TEST_PROGRAMS += test-something$X</p>
<p>all:: $(TEST_PROGRAMS)</p>
<p># GNU make supports exporting all variables by &laquo;export&raquo; without parameters.<br />
# However, the environment gets quite big, and some programs have problems<br />
# with that.</p>
<p>export NO_SVN_TESTS</p>
<p>check: common-cmds.h<br />
	if sparse; \<br />
	then \<br />
		for i in *.c */*.c; \<br />
		do \<br />
			sparse $(ALL_CFLAGS) $(SPARSE_FLAGS) $$i || exit; \<br />
		done; \<br />
	else \<br />
		echo 2>&#038;1 &laquo;Did you mean &#8216;make test&#8217;?&raquo;; \<br />
		exit 1; \<br />
	fi</p>
<p>remove-dashes:<br />
	./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)</p>
<p>### Installation rules</p>
<p>ifneq ($(filter /%,$(firstword $(template_dir))),)<br />
template_instdir = $(template_dir)<br />
else<br />
template_instdir = $(prefix)/$(template_dir)<br />
endif<br />
export template_instdir</p>
<p>ifneq ($(filter /%,$(firstword $(perfexecdir))),)<br />
perfexec_instdir = $(perfexecdir)<br />
else<br />
perfexec_instdir = $(prefix)/$(perfexecdir)<br />
endif<br />
perfexec_instdir_SQ = $(subst &#8216;,&#8217;\&raquo;,$(perfexec_instdir))<br />
export perfexec_instdir</p>
<p>install: all<br />
	$(INSTALL) -d -m 755 &#8216;$(DESTDIR_SQ)$(bindir_SQ)&#8217;<br />
	$(INSTALL) perf$X &#8216;$(DESTDIR_SQ)$(bindir_SQ)&#8217;<br />
ifdef BUILT_INS<br />
	$(INSTALL) -d -m 755 &#8216;$(DESTDIR_SQ)$(perfexec_instdir_SQ)&#8217;<br />
	$(INSTALL) $(BUILT_INS) &#8216;$(DESTDIR_SQ)$(perfexec_instdir_SQ)&#8217;<br />
ifneq (,$X)<br />
	$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) perf$X)), $(RM) &#8216;$(DESTDIR_SQ)$(perfexec_instdir_SQ)/$p&#8217;;)<br />
endif<br />
endif</p>
<p>install-doc:<br />
	$(MAKE) -C Documentation install</p>
<p>install-man:<br />
	$(MAKE) -C Documentation install-man</p>
<p>install-html:<br />
	$(MAKE) -C Documentation install-html</p>
<p>install-info:<br />
	$(MAKE) -C Documentation install-info</p>
<p>install-pdf:<br />
	$(MAKE) -C Documentation install-pdf</p>
<p>quick-install-doc:<br />
	$(MAKE) -C Documentation quick-install</p>
<p>quick-install-man:<br />
	$(MAKE) -C Documentation quick-install-man</p>
<p>quick-install-html:<br />
	$(MAKE) -C Documentation quick-install-html</p>
<p>### Maintainer&#8217;s dist rules<br />
#<br />
# None right now<br />
#<br />
#<br />
# perf.spec: perf.spec.in<br />
#	sed -e &#8217;s/@@VERSION@@/$(PERF_VERSION)/g&#8217; < $< > $@+<br />
#	mv $@+ $@<br />
#<br />
# PERF_TARNAME=perf-$(PERF_VERSION)<br />
# dist: perf.spec perf-archive$(X) configure<br />
#	./perf-archive &#8211;format=tar \<br />
#		&#8211;prefix=$(PERF_TARNAME)/ HEAD^{tree} > $(PERF_TARNAME).tar<br />
#	@mkdir -p $(PERF_TARNAME)<br />
#	@cp perf.spec configure $(PERF_TARNAME)<br />
#	@echo $(PERF_VERSION) > $(PERF_TARNAME)/version<br />
#	$(TAR) rf $(PERF_TARNAME).tar \<br />
#		$(PERF_TARNAME)/perf.spec \<br />
#		$(PERF_TARNAME)/configure \<br />
#		$(PERF_TARNAME)/version<br />
#	@$(RM) -r $(PERF_TARNAME)<br />
#	gzip -f -9 $(PERF_TARNAME).tar<br />
#<br />
# htmldocs = perf-htmldocs-$(PERF_VERSION)<br />
# manpages = perf-manpages-$(PERF_VERSION)<br />
# dist-doc:<br />
#	$(RM) -r .doc-tmp-dir<br />
#	mkdir .doc-tmp-dir<br />
#	$(MAKE) -C Documentation WEBDOC_DEST=../.doc-tmp-dir install-webdoc<br />
#	cd .doc-tmp-dir &#038;&#038; $(TAR) cf ../$(htmldocs).tar .<br />
#	gzip -n -9 -f $(htmldocs).tar<br />
#	:<br />
#	$(RM) -r .doc-tmp-dir<br />
#	mkdir -p .doc-tmp-dir/man1 .doc-tmp-dir/man5 .doc-tmp-dir/man7<br />
#	$(MAKE) -C Documentation DESTDIR=./ \<br />
#		man1dir=../.doc-tmp-dir/man1 \<br />
#		man5dir=../.doc-tmp-dir/man5 \<br />
#		man7dir=../.doc-tmp-dir/man7 \<br />
#		install<br />
#	cd .doc-tmp-dir &#038;&#038; $(TAR) cf ../$(manpages).tar .<br />
#	gzip -n -9 -f $(manpages).tar<br />
#	$(RM) -r .doc-tmp-dir<br />
#<br />
# rpm: dist<br />
#	$(RPMBUILD) -ta $(PERF_TARNAME).tar.gz</p>
<p>### Cleaning rules</p>
<p>distclean: clean<br />
#	$(RM) configure</p>
<p>clean:<br />
	$(RM) *.o */*.o $(LIB_FILE)<br />
	$(RM) $(ALL_PROGRAMS) $(BUILT_INS) perf$X<br />
	$(RM) $(TEST_PROGRAMS)<br />
	$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*<br />
	$(RM) -r autom4te.cache<br />
	$(RM) config.log config.mak.autogen config.mak.append config.status config.cache<br />
	$(RM) -r $(PERF_TARNAME) .doc-tmp-dir<br />
	$(RM) $(PERF_TARNAME).tar.gz perf-core_$(PERF_VERSION)-*.tar.gz<br />
	$(RM) $(htmldocs).tar.gz $(manpages).tar.gz<br />
	$(MAKE) -C Documentation/ clean<br />
	$(RM) PERF-VERSION-FILE PERF-CFLAGS PERF-BUILD-OPTIONS</p>
<p>.PHONY: all install clean strip<br />
.PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell<br />
.PHONY: .FORCE-PERF-VERSION-FILE TAGS tags cscope .FORCE-PERF-CFLAGS<br />
.PHONY: .FORCE-PERF-BUILD-OPTIONS</p>
<p>### Make sure built-ins do not have dups and listed in perf.c<br />
#<br />
check-builtins::<br />
	./check-builtins.sh</p>
<p>### Test suite coverage testing<br />
#<br />
# None right now<br />
#<br />
# .PHONY: coverage coverage-clean coverage-build coverage-report<br />
#<br />
# coverage:<br />
#	$(MAKE) coverage-build<br />
#	$(MAKE) coverage-report<br />
#<br />
# coverage-clean:<br />
#	rm -f *.gcda *.gcno<br />
#<br />
# COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs<br />
# COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov<br />
#<br />
# coverage-build: coverage-clean<br />
#	$(MAKE) CFLAGS=&raquo;$(COVERAGE_CFLAGS)&raquo; LDFLAGS=&raquo;$(COVERAGE_LDFLAGS)&raquo; all<br />
#	$(MAKE) CFLAGS=&raquo;$(COVERAGE_CFLAGS)&raquo; LDFLAGS=&raquo;$(COVERAGE_LDFLAGS)&raquo; \<br />
#		-j1 test<br />
#<br />
# coverage-report:<br />
#	gcov -b *.c */*.c<br />
#	grep &#8216;^function.*called 0 &#8216; *.c.gcov */*.c.gcov \<br />
#		| sed -e &#8217;s/\([^:]*\)\.gcov: *function \([^ ]*\) called.*/\1: \2/&#8217; \<br />
#		| tee coverage-untested-functions</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/makefile-2/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>design.txt</title>
		<link>http://lynyrd.ru/design-txt</link>
		<comments>http://lynyrd.ru/design-txt#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:37:13 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=617</guid>
		<description><![CDATA[Most of the infrastructure that &#8216;perf&#8217; uses here has been reused
from the Git project, as of version:
    66996ec: Sync with 1.6.2.4
Here is an (incomplete!) list of main contributors to those files
in util/* and elsewhere:
 Alex Riesen
 Christian Couder
 Dmitry Potapov
 Jeff King
 Johannes Schindelin
 Johannes Sixt
 Junio C Hamano
 Linus Torvalds
 Matthias ]]></description>
			<content:encoded><![CDATA[<p>Most of the infrastructure that &#8216;perf&#8217; uses here has been reused<br />
from the Git project, as of version:<span id="more-617"></span></p>
<p>    66996ec: Sync with 1.6.2.4</p>
<p>Here is an (incomplete!) list of main contributors to those files<br />
in util/* and elsewhere:</p>
<p> Alex Riesen<br />
 Christian Couder<br />
 Dmitry Potapov<br />
 Jeff King<br />
 Johannes Schindelin<br />
 Johannes Sixt<br />
 Junio C Hamano<br />
 Linus Torvalds<br />
 Matthias Kestenholz<br />
 Michal Ostrowski<br />
 Miklos Vajna<br />
 Petr Baudis<br />
 Pierre Habouzit<br />
 René Scharfe<br />
 Samuel Tardieu<br />
 Shawn O. Pearce<br />
 Steffen Prohaska<br />
 Steve Haslam</p>
<p>Thanks guys!</p>
<p>The full history of the files can be found in the upstream Git commits.</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/design-txt/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>CREDITS</title>
		<link>http://lynyrd.ru/credits</link>
		<comments>http://lynyrd.ru/credits#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:35:27 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=615</guid>
		<description><![CDATA[Most of the infrastructure that &#8216;perf&#8217; uses here has been reused
from the Git project, as of version:
    66996ec: Sync with 1.6.2.4
Here is an (incomplete!) list of main contributors to those files
in util/* and elsewhere:
 Alex Riesen
 Christian Couder
 Dmitry Potapov
 Jeff King
 Johannes Schindelin
 Johannes Sixt
 Junio C Hamano
 Linus Torvalds
 Matthias ]]></description>
			<content:encoded><![CDATA[<p>Most of the infrastructure that &#8216;perf&#8217; uses here has been reused<br />
from the Git project, as of version:<span id="more-615"></span></p>
<p>    66996ec: Sync with 1.6.2.4</p>
<p>Here is an (incomplete!) list of main contributors to those files<br />
in util/* and elsewhere:</p>
<p> Alex Riesen<br />
 Christian Couder<br />
 Dmitry Potapov<br />
 Jeff King<br />
 Johannes Schindelin<br />
 Johannes Sixt<br />
 Junio C Hamano<br />
 Linus Torvalds<br />
 Matthias Kestenholz<br />
 Michal Ostrowski<br />
 Miklos Vajna<br />
 Petr Baudis<br />
 Pierre Habouzit<br />
 René Scharfe<br />
 Samuel Tardieu<br />
 Shawn O. Pearce<br />
 Steffen Prohaska<br />
 Steve Haslam</p>
<p>Thanks guys!</p>
<p>The full history of the files can be found in the upstream Git commits.</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/credits/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>command-list.txt</title>
		<link>http://lynyrd.ru/command-list-txt</link>
		<comments>http://lynyrd.ru/command-list-txt#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:32:43 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/command-list-txt</guid>
		<description><![CDATA[#
# List of known perf commands.
# command name			category [deprecated] [common]
#
perf-annotate			mainporcelain common
perf-list			mainporcelain common
perf-sched			mainporcelain common
perf-record			mainporcelain common
perf-report			mainporcelain common
perf-stat			mainporcelain common
perf-timechart			mainporcelain common
perf-top			mainporcelain common
perf-trace			mainporcelain common
]]></description>
			<content:encoded><![CDATA[<p>#<br />
# List of known perf commands.<span id="more-614"></span><br />
# command name			category [deprecated] [common]<br />
#<br />
perf-annotate			mainporcelain common<br />
perf-list			mainporcelain common<br />
perf-sched			mainporcelain common<br />
perf-record			mainporcelain common<br />
perf-report			mainporcelain common<br />
perf-stat			mainporcelain common<br />
perf-timechart			mainporcelain common<br />
perf-top			mainporcelain common<br />
perf-trace			mainporcelain common</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/command-list-txt/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin.h</title>
		<link>http://lynyrd.ru/builtin-h</link>
		<comments>http://lynyrd.ru/builtin-h#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:31:00 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/builtin-h</guid>
		<description><![CDATA[#ifndef BUILTIN_H
#define BUILTIN_H
#include &#171;util/util.h&#187;
#include &#171;util/strbuf.h&#187;
extern const char perf_version_string[];
extern const char perf_usage_string[];
extern const char perf_more_info_string[];
extern void list_common_cmds_help(void);
extern const char *help_unknown_cmd(const char *cmd);
extern void prune_packed_objects(int);
extern int read_line_with_nul(char *buf, int size, FILE *file);
extern int check_pager_config(const char *cmd);
extern int cmd_annotate(int argc, const char **argv, const char *prefix);
extern int cmd_help(int argc, const char **argv, const char *prefix);
extern int cmd_sched(int argc, ]]></description>
			<content:encoded><![CDATA[<p>#ifndef BUILTIN_H<br />
#define BUILTIN_H<span id="more-612"></span></p>
<p>#include &laquo;util/util.h&raquo;<br />
#include &laquo;util/strbuf.h&raquo;</p>
<p>extern const char perf_version_string[];<br />
extern const char perf_usage_string[];<br />
extern const char perf_more_info_string[];</p>
<p>extern void list_common_cmds_help(void);<br />
extern const char *help_unknown_cmd(const char *cmd);<br />
extern void prune_packed_objects(int);<br />
extern int read_line_with_nul(char *buf, int size, FILE *file);<br />
extern int check_pager_config(const char *cmd);</p>
<p>extern int cmd_annotate(int argc, const char **argv, const char *prefix);<br />
extern int cmd_help(int argc, const char **argv, const char *prefix);<br />
extern int cmd_sched(int argc, const char **argv, const char *prefix);<br />
extern int cmd_list(int argc, const char **argv, const char *prefix);<br />
extern int cmd_record(int argc, const char **argv, const char *prefix);<br />
extern int cmd_report(int argc, const char **argv, const char *prefix);<br />
extern int cmd_stat(int argc, const char **argv, const char *prefix);<br />
extern int cmd_timechart(int argc, const char **argv, const char *prefix);<br />
extern int cmd_top(int argc, const char **argv, const char *prefix);<br />
extern int cmd_trace(int argc, const char **argv, const char *prefix);<br />
extern int cmd_version(int argc, const char **argv, const char *prefix);</p>
<p>#endif</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-h/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-top.c</title>
		<link>http://lynyrd.ru/builtin-top-c</link>
		<comments>http://lynyrd.ru/builtin-top-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:28:53 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/builtin-top-c</guid>
		<description><![CDATA[/*
 * builtin-top.c
 *
 * Builtin top command: Display a continuously updated profile of
 * any workload, CPU or specific PID.
 *
 * Copyright (C) 2008, Red Hat Inc, Ingo Molnar 
 *
 * Improvements and fixes by:
 *
 *   Arjan van de Ven 
 *   Yanmin Zhang 
 *  ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-top.c<span id="more-609"></span><br />
 *<br />
 * Builtin top command: Display a continuously updated profile of<br />
 * any workload, CPU or specific PID.<br />
 *<br />
 * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com><br />
 *<br />
 * Improvements and fixes by:<br />
 *<br />
 *   Arjan van de Ven <arjan@linux.intel.com><br />
 *   Yanmin Zhang <yanmin.zhang@intel.com><br />
 *   Wu Fengguang <fengguang.wu@intel.com><br />
 *   Mike Galbraith <efault@gmx.de><br />
 *   Paul Mackerras
<paulus@samba.org>
 *<br />
 * Released under the GPL v2. (and only v2, not any later version)<br />
 */<br />
#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;perf.h&raquo;</p>
<p>#include &laquo;util/symbol.h&raquo;<br />
#include &laquo;util/color.h&raquo;<br />
#include &laquo;util/util.h&raquo;<br />
#include
<linux/rbtree.h>
#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;</p>
<p>#include &laquo;util/debug.h&raquo;</p>
<p>#include <assert.h><br />
#include <fcntl.h></p>
<p>#include <stdio.h><br />
#include <termios.h><br />
#include <unistd.h></p>
<p>#include <errno.h><br />
#include <time.h><br />
#include <sched.h><br />
#include
<pthread.h>
<p>#include <sys/syscall.h><br />
#include <sys/ioctl.h><br />
#include <sys/poll.h><br />
#include <sys/prctl.h><br />
#include <sys/wait.h><br />
#include <sys/uio.h><br />
#include <sys/mman.h></p>
<p>#include
<linux/unistd.h>
#include
<linux/types.h>
<p>static int			fd[MAX_NR_CPUS][MAX_COUNTERS];</p>
<p>static int			system_wide			=  0;</p>
<p>static int			default_interval		= 100000;</p>
<p>static int			count_filter			=  5;<br />
static int			print_entries			= 15;</p>
<p>static int			target_pid			= -1;<br />
static int			inherit				=  0;<br />
static int			profile_cpu			= -1;<br />
static int			nr_cpus				=  0;<br />
static unsigned int		realtime_prio			=  0;<br />
static int			group				=  0;<br />
static unsigned int		page_size;<br />
static unsigned int		mmap_pages			= 16;<br />
static int			freq				=  0;</p>
<p>static int			delay_secs			=  2;<br />
static int			zero;<br />
static int			dump_symtab;</p>
<p>/*<br />
 * Source<br />
 */</p>
<p>struct source_line {<br />
	u64			eip;<br />
	unsigned long		count[MAX_COUNTERS];<br />
	char			*line;<br />
	struct source_line	*next;<br />
};</p>
<p>static char			*sym_filter			=  NULL;<br />
struct sym_entry		*sym_filter_entry		=  NULL;<br />
static int			sym_pcnt_filter			=  5;<br />
static int			sym_counter			=  0;<br />
static int			display_weighted		= -1;</p>
<p>/*<br />
 * Symbols<br />
 */</p>
<p>static u64			min_ip;<br />
static u64			max_ip = -1ll;</p>
<p>struct sym_entry {<br />
	struct rb_node		rb_node;<br />
	struct list_head	node;<br />
	unsigned long		count[MAX_COUNTERS];<br />
	unsigned long		snap_count;<br />
	double			weight;<br />
	int			skip;<br />
	struct source_line	*source;<br />
	struct source_line	*lines;<br />
	struct source_line	**lines_tail;<br />
	pthread_mutex_t		source_lock;<br />
};</p>
<p>/*<br />
 * Source functions<br />
 */</p>
<p>static void parse_source(struct sym_entry *syme)<br />
{<br />
	struct symbol *sym;<br />
	struct module *module;<br />
	struct section *section = NULL;<br />
	FILE *file;<br />
	char command[PATH_MAX*2];<br />
	const char *path = vmlinux_name;<br />
	u64 start, end, len;</p>
<p>	if (!syme)<br />
		return;</p>
<p>	if (syme->lines) {<br />
		pthread_mutex_lock(&#038;syme->source_lock);<br />
		goto out_assign;<br />
	}</p>
<p>	sym = (struct symbol *)(syme + 1);<br />
	module = sym->module;</p>
<p>	if (module)<br />
		path = module->path;<br />
	if (!path)<br />
		return;</p>
<p>	start = sym->obj_start;<br />
	if (!start)<br />
		start = sym->start;</p>
<p>	if (module) {<br />
		section = module->sections->find_section(module->sections, &laquo;.text&raquo;);<br />
		if (section)<br />
			start -= section->vma;<br />
	}</p>
<p>	end = start + sym->end &#8211; sym->start + 1;<br />
	len = sym->end &#8211; sym->start;</p>
<p>	sprintf(command, &laquo;objdump &#8211;start-address=0x%016Lx &#8211;stop-address=0x%016Lx -dS %s&raquo;, start, end, path);</p>
<p>	file = popen(command, &laquo;r&raquo;);<br />
	if (!file)<br />
		return;</p>
<p>	pthread_mutex_lock(&#038;syme->source_lock);<br />
	syme->lines_tail = &#038;syme->lines;<br />
	while (!feof(file)) {<br />
		struct source_line *src;<br />
		size_t dummy = 0;<br />
		char *c;</p>
<p>		src = malloc(sizeof(struct source_line));<br />
		assert(src != NULL);<br />
		memset(src, 0, sizeof(struct source_line));</p>
<p>		if (getline(&#038;src->line, &#038;dummy, file) < 0)<br />
			break;<br />
		if (!src->line)<br />
			break;</p>
<p>		c = strchr(src->line, &#8216;\n&#8217;);<br />
		if (c)<br />
			*c = 0;</p>
<p>		src->next = NULL;<br />
		*syme->lines_tail = src;<br />
		syme->lines_tail = &#038;src->next;</p>
<p>		if (strlen(src->line)>8 &#038;&#038; src->line[8] == &#8216;:&#8217;) {<br />
			src->eip = strtoull(src->line, NULL, 16);<br />
			if (section)<br />
				src->eip += section->vma;<br />
		}<br />
		if (strlen(src->line)>8 &#038;&#038; src->line[16] == &#8216;:&#8217;) {<br />
			src->eip = strtoull(src->line, NULL, 16);<br />
			if (section)<br />
				src->eip += section->vma;<br />
		}<br />
	}<br />
	pclose(file);<br />
out_assign:<br />
	sym_filter_entry = syme;<br />
	pthread_mutex_unlock(&#038;syme->source_lock);<br />
}</p>
<p>static void __zero_source_counters(struct sym_entry *syme)<br />
{<br />
	int i;<br />
	struct source_line *line;</p>
<p>	line = syme->lines;<br />
	while (line) {<br />
		for (i = 0; i < nr_counters; i++)<br />
			line->count[i] = 0;<br />
		line = line->next;<br />
	}<br />
}</p>
<p>static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)<br />
{<br />
	struct source_line *line;</p>
<p>	if (syme != sym_filter_entry)<br />
		return;</p>
<p>	if (pthread_mutex_trylock(&#038;syme->source_lock))<br />
		return;</p>
<p>	if (!syme->source)<br />
		goto out_unlock;</p>
<p>	for (line = syme->lines; line; line = line->next) {<br />
		if (line->eip == ip) {<br />
			line->count[counter]++;<br />
			break;<br />
		}<br />
		if (line->eip > ip)<br />
			break;<br />
	}<br />
out_unlock:<br />
	pthread_mutex_unlock(&#038;syme->source_lock);<br />
}</p>
<p>static void lookup_sym_source(struct sym_entry *syme)<br />
{<br />
	struct symbol *symbol = (struct symbol *)(syme + 1);<br />
	struct source_line *line;<br />
	char pattern[PATH_MAX];<br />
	char *idx;</p>
<p>	sprintf(pattern, &laquo;<%s>:&raquo;, symbol->name);</p>
<p>	if (symbol->module) {<br />
		idx = strstr(pattern, &laquo;\t&raquo;);<br />
		if (idx)<br />
			*idx = 0;<br />
	}</p>
<p>	pthread_mutex_lock(&#038;syme->source_lock);<br />
	for (line = syme->lines; line; line = line->next) {<br />
		if (strstr(line->line, pattern)) {<br />
			syme->source = line;<br />
			break;<br />
		}<br />
	}<br />
	pthread_mutex_unlock(&#038;syme->source_lock);<br />
}</p>
<p>static void show_lines(struct source_line *queue, int count, int total)<br />
{<br />
	int i;<br />
	struct source_line *line;</p>
<p>	line = queue;<br />
	for (i = 0; i < count; i++) {<br />
		float pcnt = 100.0*(float)line->count[sym_counter]/(float)total;</p>
<p>		printf(&raquo;%8li %4.1f%%\t%s\n&raquo;, line->count[sym_counter], pcnt, line->line);<br />
		line = line->next;<br />
	}<br />
}</p>
<p>#define TRACE_COUNT     3</p>
<p>static void show_details(struct sym_entry *syme)<br />
{<br />
	struct symbol *symbol;<br />
	struct source_line *line;<br />
	struct source_line *line_queue = NULL;<br />
	int displayed = 0;<br />
	int line_queue_count = 0, total = 0, more = 0;</p>
<p>	if (!syme)<br />
		return;</p>
<p>	if (!syme->source)<br />
		lookup_sym_source(syme);</p>
<p>	if (!syme->source)<br />
		return;</p>
<p>	symbol = (struct symbol *)(syme + 1);<br />
	printf(&raquo;Showing %s for %s\n&raquo;, event_name(sym_counter), symbol->name);<br />
	printf(&raquo;  Events  Pcnt (>=%d%%)\n&raquo;, sym_pcnt_filter);</p>
<p>	pthread_mutex_lock(&#038;syme->source_lock);<br />
	line = syme->source;<br />
	while (line) {<br />
		total += line->count[sym_counter];<br />
		line = line->next;<br />
	}</p>
<p>	line = syme->source;<br />
	while (line) {<br />
		float pcnt = 0.0;</p>
<p>		if (!line_queue_count)<br />
			line_queue = line;<br />
		line_queue_count++;</p>
<p>		if (line->count[sym_counter])<br />
			pcnt = 100.0 * line->count[sym_counter] / (float)total;<br />
		if (pcnt >= (float)sym_pcnt_filter) {<br />
			if (displayed <= print_entries)<br />
				show_lines(line_queue, line_queue_count, total);<br />
			else more++;<br />
			displayed += line_queue_count;<br />
			line_queue_count = 0;<br />
			line_queue = NULL;<br />
		} else if (line_queue_count > TRACE_COUNT) {<br />
			line_queue = line_queue->next;<br />
			line_queue_count&#8211;;<br />
		}</p>
<p>		line->count[sym_counter] = zero ? 0 : line->count[sym_counter] * 7 / 8;<br />
		line = line->next;<br />
	}<br />
	pthread_mutex_unlock(&#038;syme->source_lock);<br />
	if (more)<br />
		printf(&raquo;%d lines not displayed, maybe increase display entries [e]\n&raquo;, more);<br />
}</p>
<p>/*<br />
 * Symbols will be added here in record_ip and will get out<br />
 * after decayed.<br />
 */<br />
static LIST_HEAD(active_symbols);<br />
static pthread_mutex_t active_symbols_lock = PTHREAD_MUTEX_INITIALIZER;</p>
<p>/*<br />
 * Ordering weight: count-1 * count-2 * &#8230; / count-n<br />
 */<br />
static double sym_weight(const struct sym_entry *sym)<br />
{<br />
	double weight = sym->snap_count;<br />
	int counter;</p>
<p>	if (!display_weighted)<br />
		return weight;</p>
<p>	for (counter = 1; counter < nr_counters-1; counter++)<br />
		weight *= sym->count[counter];</p>
<p>	weight /= (sym->count[counter] + 1);</p>
<p>	return weight;<br />
}</p>
<p>static long			samples;<br />
static long			userspace_samples;<br />
static const char		CONSOLE_CLEAR[] = &laquo;[H[2J";</p>
<p>static void __list_insert_active_sym(struct sym_entry *syme)<br />
{<br />
	list_add(&#038;syme->node, &#038;active_symbols);<br />
}</p>
<p>static void list_remove_active_sym(struct sym_entry *syme)<br />
{<br />
	pthread_mutex_lock(&#038;active_symbols_lock);<br />
	list_del_init(&#038;syme->node);<br />
	pthread_mutex_unlock(&#038;active_symbols_lock);<br />
}</p>
<p>static void rb_insert_active_sym(struct rb_root *tree, struct sym_entry *se)<br />
{<br />
	struct rb_node **p = &#038;tree->rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct sym_entry *iter;</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		iter = rb_entry(parent, struct sym_entry, rb_node);</p>
<p>		if (se->weight > iter->weight)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;se->rb_node, parent, p);<br />
	rb_insert_color(&#038;se->rb_node, tree);<br />
}</p>
<p>static void print_sym_table(void)<br />
{<br />
	int printed = 0, j;<br />
	int counter, snap = !display_weighted ? sym_counter : 0;<br />
	float samples_per_sec = samples/delay_secs;<br />
	float ksamples_per_sec = (samples-userspace_samples)/delay_secs;<br />
	float sum_ksamples = 0.0;<br />
	struct sym_entry *syme, *n;<br />
	struct rb_root tmp = RB_ROOT;<br />
	struct rb_node *nd;</p>
<p>	samples = userspace_samples = 0;</p>
<p>	/* Sort the active symbols */<br />
	pthread_mutex_lock(&#038;active_symbols_lock);<br />
	syme = list_entry(active_symbols.next, struct sym_entry, node);<br />
	pthread_mutex_unlock(&#038;active_symbols_lock);</p>
<p>	list_for_each_entry_safe_from(syme, n, &#038;active_symbols, node) {<br />
		syme->snap_count = syme->count[snap];<br />
		if (syme->snap_count != 0) {<br />
			syme->weight = sym_weight(syme);<br />
			rb_insert_active_sym(&#038;tmp, syme);<br />
			sum_ksamples += syme->snap_count;</p>
<p>			for (j = 0; j < nr_counters; j++)<br />
				syme->count[j] = zero ? 0 : syme->count[j] * 7 / 8;<br />
		} else<br />
			list_remove_active_sym(syme);<br />
	}</p>
<p>	puts(CONSOLE_CLEAR);</p>
<p>	printf(<br />
&laquo;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;\n&raquo;);<br />
	printf( &raquo;   PerfTop:%8.0f irqs/sec  kernel:%4.1f%% [",<br />
		samples_per_sec,<br />
		100.0 - (100.0*((samples_per_sec-ksamples_per_sec)/samples_per_sec)));</p>
<p>	if (nr_counters == 1 || !display_weighted) {<br />
		printf("%Ld", (u64)attrs[0].sample_period);<br />
		if (freq)<br />
			printf(&raquo;Hz &laquo;);<br />
		else<br />
			printf(&raquo; &laquo;);<br />
	}</p>
<p>	if (!display_weighted)<br />
		printf(&raquo;%s&raquo;, event_name(sym_counter));<br />
	else for (counter = 0; counter < nr_counters; counter++) {<br />
		if (counter)<br />
			printf("/");</p>
<p>		printf("%s", event_name(counter));<br />
	}</p>
<p>	printf( "], ");</p>
<p>	if (target_pid != -1)<br />
		printf(" (target_pid: %d", target_pid);<br />
	else<br />
		printf(" (all");</p>
<p>	if (profile_cpu != -1)<br />
		printf(", cpu: %d)\n", profile_cpu);<br />
	else {<br />
		if (target_pid != -1)<br />
			printf(")\n");<br />
		else<br />
			printf(", %d CPUs)\n", nr_cpus);<br />
	}</p>
<p>	printf("------------------------------------------------------------------------------\n\n");</p>
<p>	if (sym_filter_entry) {<br />
		show_details(sym_filter_entry);<br />
		return;<br />
	}</p>
<p>	if (nr_counters == 1)<br />
		printf("             samples    pcnt");<br />
	else<br />
		printf("   weight    samples    pcnt");</p>
<p>	if (verbose)<br />
		printf("         RIP       ");<br />
	printf("   kernel function\n");<br />
	printf("   %s    _______   _____",<br />
	       nr_counters == 1 ? "      " : "______");<br />
	if (verbose)<br />
		printf("   ________________");<br />
	printf("   _______________\n\n");</p>
<p>	for (nd = rb_first(&#038;tmp); nd; nd = rb_next(nd)) {<br />
		struct symbol *sym;<br />
		double pcnt;</p>
<p>		syme = rb_entry(nd, struct sym_entry, rb_node);<br />
		sym = (struct symbol *)(syme + 1);</p>
<p>		if (++printed > print_entries || (int)syme->snap_count < count_filter)<br />
			continue;</p>
<p>		pcnt = 100.0 - (100.0 * ((sum_ksamples - syme->snap_count) /<br />
					 sum_ksamples));</p>
<p>		if (nr_counters == 1 || !display_weighted)<br />
			printf(&raquo;%20.2f &#8211; &laquo;, syme->weight);<br />
		else<br />
			printf(&raquo;%9.1f %10ld &#8211; &laquo;, syme->weight, syme->snap_count);</p>
<p>		percent_color_fprintf(stdout, &laquo;%4.1f%%&raquo;, pcnt);<br />
		if (verbose)<br />
			printf(&raquo; &#8211; %016llx&raquo;, sym->start);<br />
		printf(&raquo; : %s&raquo;, sym->name);<br />
		if (sym->module)<br />
			printf(&raquo;\t[%s]&laquo;, sym->module->name);<br />
		printf(&raquo;\n&raquo;);<br />
	}<br />
}</p>
<p>static void prompt_integer(int *target, const char *msg)<br />
{<br />
	char *buf = malloc(0), *p;<br />
	size_t dummy = 0;<br />
	int tmp;</p>
<p>	fprintf(stdout, &laquo;\n%s: &laquo;, msg);<br />
	if (getline(&#038;buf, &#038;dummy, stdin) < 0)<br />
		return;</p>
<p>	p = strchr(buf, '\n');<br />
	if (p)<br />
		*p = 0;</p>
<p>	p = buf;<br />
	while(*p) {<br />
		if (!isdigit(*p))<br />
			goto out_free;<br />
		p++;<br />
	}<br />
	tmp = strtoul(buf, NULL, 10);<br />
	*target = tmp;<br />
out_free:<br />
	free(buf);<br />
}</p>
<p>static void prompt_percent(int *target, const char *msg)<br />
{<br />
	int tmp = 0;</p>
<p>	prompt_integer(&#038;tmp, msg);<br />
	if (tmp >= 0 &#038;&#038; tmp <= 100)<br />
		*target = tmp;<br />
}</p>
<p>static void prompt_symbol(struct sym_entry **target, const char *msg)<br />
{<br />
	char *buf = malloc(0), *p;<br />
	struct sym_entry *syme = *target, *n, *found = NULL;<br />
	size_t dummy = 0;</p>
<p>	/* zero counters of active symbol */<br />
	if (syme) {<br />
		pthread_mutex_lock(&#038;syme->source_lock);<br />
		__zero_source_counters(syme);<br />
		*target = NULL;<br />
		pthread_mutex_unlock(&#038;syme->source_lock);<br />
	}</p>
<p>	fprintf(stdout, &laquo;\n%s: &laquo;, msg);<br />
	if (getline(&#038;buf, &#038;dummy, stdin) < 0)<br />
		goto out_free;</p>
<p>	p = strchr(buf, '\n');<br />
	if (p)<br />
		*p = 0;</p>
<p>	pthread_mutex_lock(&#038;active_symbols_lock);<br />
	syme = list_entry(active_symbols.next, struct sym_entry, node);<br />
	pthread_mutex_unlock(&#038;active_symbols_lock);</p>
<p>	list_for_each_entry_safe_from(syme, n, &#038;active_symbols, node) {<br />
		struct symbol *sym = (struct symbol *)(syme + 1);</p>
<p>		if (!strcmp(buf, sym->name)) {<br />
			found = syme;<br />
			break;<br />
		}<br />
	}</p>
<p>	if (!found) {<br />
		fprintf(stderr, &laquo;Sorry, %s is not active.\n&raquo;, sym_filter);<br />
		sleep(1);<br />
		return;<br />
	} else<br />
		parse_source(found);</p>
<p>out_free:<br />
	free(buf);<br />
}</p>
<p>static void print_mapped_keys(void)<br />
{<br />
	char *name = NULL;</p>
<p>	if (sym_filter_entry) {<br />
		struct symbol *sym = (struct symbol *)(sym_filter_entry+1);<br />
		name = sym->name;<br />
	}</p>
<p>	fprintf(stdout, &laquo;\nMapped keys:\n&raquo;);<br />
	fprintf(stdout, &laquo;\t[d]     display refresh delay.             \t(%d)\n&raquo;, delay_secs);<br />
	fprintf(stdout, &laquo;\t[e]     display entries (lines).           \t(%d)\n&raquo;, print_entries);</p>
<p>	if (nr_counters > 1)<br />
		fprintf(stdout, &laquo;\t[E]     active event counter.              \t(%s)\n&raquo;, event_name(sym_counter));</p>
<p>	fprintf(stdout, &laquo;\t[f]     profile display filter (count).    \t(%d)\n&raquo;, count_filter);</p>
<p>	if (vmlinux_name) {<br />
		fprintf(stdout, &laquo;\t[F]     annotate display filter (percent). \t(%d%%)\n&raquo;, sym_pcnt_filter);<br />
		fprintf(stdout, &laquo;\t[s]     annotate symbol.                   \t(%s)\n&raquo;, name?: &laquo;NULL&raquo;);<br />
		fprintf(stdout, &laquo;\t[S]     stop annotation.\n&raquo;);<br />
	}</p>
<p>	if (nr_counters > 1)<br />
		fprintf(stdout, &laquo;\t[w]     toggle display weighted/count[E]r. \t(%d)\n&raquo;, display_weighted ? 1 : 0);</p>
<p>	fprintf(stdout, &laquo;\t[z]     toggle sample zeroing.             \t(%d)\n&raquo;, zero ? 1 : 0);<br />
	fprintf(stdout, &laquo;\t[qQ]    quit.\n&raquo;);<br />
}</p>
<p>static int key_mapped(int c)<br />
{<br />
	switch (c) {<br />
		case &#8216;d&#8217;:<br />
		case &#8216;e&#8217;:<br />
		case &#8216;f&#8217;:<br />
		case &#8216;z&#8217;:<br />
		case &#8216;q&#8217;:<br />
		case &#8216;Q&#8217;:<br />
			return 1;<br />
		case &#8216;E&#8217;:<br />
		case &#8216;w&#8217;:<br />
			return nr_counters > 1 ? 1 : 0;<br />
		case &#8216;F&#8217;:<br />
		case &#8217;s&#8217;:<br />
		case &#8216;S&#8217;:<br />
			return vmlinux_name ? 1 : 0;<br />
		default:<br />
			break;<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static void handle_keypress(int c)<br />
{<br />
	if (!key_mapped(c)) {<br />
		struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };<br />
		struct termios tc, save;</p>
<p>		print_mapped_keys();<br />
		fprintf(stdout, &laquo;\nEnter selection, or unmapped key to continue: &laquo;);<br />
		fflush(stdout);</p>
<p>		tcgetattr(0, &#038;save);<br />
		tc = save;<br />
		tc.c_lflag &#038;= ~(ICANON | ECHO);<br />
		tc.c_cc[VMIN] = 0;<br />
		tc.c_cc[VTIME] = 0;<br />
		tcsetattr(0, TCSANOW, &#038;tc);</p>
<p>		poll(&#038;stdin_poll, 1, -1);<br />
		c = getc(stdin);</p>
<p>		tcsetattr(0, TCSAFLUSH, &#038;save);<br />
		if (!key_mapped(c))<br />
			return;<br />
	}</p>
<p>	switch (c) {<br />
		case &#8216;d&#8217;:<br />
			prompt_integer(&#038;delay_secs, &laquo;Enter display delay&raquo;);<br />
			if (delay_secs < 1)<br />
				delay_secs = 1;<br />
			break;<br />
		case 'e':<br />
			prompt_integer(&#038;print_entries, "Enter display entries (lines)");<br />
			break;<br />
		case 'E':<br />
			if (nr_counters > 1) {<br />
				int i;</p>
<p>				fprintf(stderr, &laquo;\nAvailable events:&raquo;);<br />
				for (i = 0; i < nr_counters; i++)<br />
					fprintf(stderr, "\n\t%d %s", i, event_name(i));</p>
<p>				prompt_integer(&#038;sym_counter, "Enter details event counter");</p>
<p>				if (sym_counter >= nr_counters) {<br />
					fprintf(stderr, &laquo;Sorry, no such event, using %s.\n&raquo;, event_name(0));<br />
					sym_counter = 0;<br />
					sleep(1);<br />
				}<br />
			} else sym_counter = 0;<br />
			break;<br />
		case &#8216;f&#8217;:<br />
			prompt_integer(&#038;count_filter, &laquo;Enter display event count filter&raquo;);<br />
			break;<br />
		case &#8216;F&#8217;:<br />
			prompt_percent(&#038;sym_pcnt_filter, &laquo;Enter details display event filter (percent)&raquo;);<br />
			break;<br />
		case &#8216;q&#8217;:<br />
		case &#8216;Q&#8217;:<br />
			printf(&raquo;exiting.\n&raquo;);<br />
			exit(0);<br />
		case &#8217;s&#8217;:<br />
			prompt_symbol(&#038;sym_filter_entry, &laquo;Enter details symbol&raquo;);<br />
			break;<br />
		case &#8216;S&#8217;:<br />
			if (!sym_filter_entry)<br />
				break;<br />
			else {<br />
				struct sym_entry *syme = sym_filter_entry;</p>
<p>				pthread_mutex_lock(&#038;syme->source_lock);<br />
				sym_filter_entry = NULL;<br />
				__zero_source_counters(syme);<br />
				pthread_mutex_unlock(&#038;syme->source_lock);<br />
			}<br />
			break;<br />
		case &#8216;w&#8217;:<br />
			display_weighted = ~display_weighted;<br />
			break;<br />
		case &#8216;z&#8217;:<br />
			zero = ~zero;<br />
			break;<br />
		default:<br />
			break;<br />
	}<br />
}</p>
<p>static void *display_thread(void *arg __used)<br />
{<br />
	struct pollfd stdin_poll = { .fd = 0, .events = POLLIN };<br />
	struct termios tc, save;<br />
	int delay_msecs, c;</p>
<p>	tcgetattr(0, &#038;save);<br />
	tc = save;<br />
	tc.c_lflag &#038;= ~(ICANON | ECHO);<br />
	tc.c_cc[VMIN] = 0;<br />
	tc.c_cc[VTIME] = 0;</p>
<p>repeat:<br />
	delay_msecs = delay_secs * 1000;<br />
	tcsetattr(0, TCSANOW, &#038;tc);<br />
	/* trash return*/<br />
	getc(stdin);</p>
<p>	do {<br />
		print_sym_table();<br />
	} while (!poll(&#038;stdin_poll, 1, delay_msecs) == 1);</p>
<p>	c = getc(stdin);<br />
	tcsetattr(0, TCSAFLUSH, &#038;save);</p>
<p>	handle_keypress(c);<br />
	goto repeat;</p>
<p>	return NULL;<br />
}</p>
<p>/* Tag samples to be skipped. */<br />
static const char *skip_symbols[] = {<br />
	&laquo;default_idle&raquo;,<br />
	&laquo;cpu_idle&raquo;,<br />
	&laquo;enter_idle&raquo;,<br />
	&laquo;exit_idle&raquo;,<br />
	&laquo;mwait_idle&raquo;,<br />
	&laquo;mwait_idle_with_hints&raquo;,<br />
	&laquo;poll_idle&raquo;,<br />
	&laquo;ppc64_runlatch_off&raquo;,<br />
	&laquo;pseries_dedicated_idle_sleep&raquo;,<br />
	NULL<br />
};</p>
<p>static int symbol_filter(struct dso *self, struct symbol *sym)<br />
{<br />
	struct sym_entry *syme;<br />
	const char *name = sym->name;<br />
	int i;</p>
<p>	/*<br />
	 * ppc64 uses function descriptors and appends a &#8216;.&#8217; to the<br />
	 * start of every instruction address. Remove it.<br />
	 */<br />
	if (name[0] == &#8216;.&#8217;)<br />
		name++;</p>
<p>	if (!strcmp(name, &laquo;_text&raquo;) ||<br />
	    !strcmp(name, &laquo;_etext&raquo;) ||<br />
	    !strcmp(name, &laquo;_sinittext&raquo;) ||<br />
	    !strncmp(&raquo;init_module&raquo;, name, 11) ||<br />
	    !strncmp(&raquo;cleanup_module&raquo;, name, 14) ||<br />
	    strstr(name, &laquo;_text_start&raquo;) ||<br />
	    strstr(name, &laquo;_text_end&raquo;))<br />
		return 1;</p>
<p>	syme = dso__sym_priv(self, sym);<br />
	pthread_mutex_init(&#038;syme->source_lock, NULL);<br />
	if (!sym_filter_entry &#038;&#038; sym_filter &#038;&#038; !strcmp(name, sym_filter))<br />
		sym_filter_entry = syme;</p>
<p>	for (i = 0; skip_symbols[i]; i++) {<br />
		if (!strcmp(skip_symbols[i], name)) {<br />
			syme->skip = 1;<br />
			break;<br />
		}<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static int parse_symbols(void)<br />
{<br />
	struct rb_node *node;<br />
	struct symbol  *sym;<br />
	int use_modules = vmlinux_name ? 1 : 0;</p>
<p>	kernel_dso = dso__new(&raquo;[kernel]&laquo;, sizeof(struct sym_entry));<br />
	if (kernel_dso == NULL)<br />
		return -1;</p>
<p>	if (dso__load_kernel(kernel_dso, vmlinux_name, symbol_filter, verbose, use_modules) <= 0)<br />
		goto out_delete_dso;</p>
<p>	node = rb_first(&#038;kernel_dso->syms);<br />
	sym = rb_entry(node, struct symbol, rb_node);<br />
	min_ip = sym->start;</p>
<p>	node = rb_last(&#038;kernel_dso->syms);<br />
	sym = rb_entry(node, struct symbol, rb_node);<br />
	max_ip = sym->end;</p>
<p>	if (dump_symtab)<br />
		dso__fprintf(kernel_dso, stderr);</p>
<p>	return 0;</p>
<p>out_delete_dso:<br />
	dso__delete(kernel_dso);<br />
	kernel_dso = NULL;<br />
	return -1;<br />
}</p>
<p>/*<br />
 * Binary search in the histogram table and record the hit:<br />
 */<br />
static void record_ip(u64 ip, int counter)<br />
{<br />
	struct symbol *sym = dso__find_symbol(kernel_dso, ip);</p>
<p>	if (sym != NULL) {<br />
		struct sym_entry *syme = dso__sym_priv(kernel_dso, sym);</p>
<p>		if (!syme->skip) {<br />
			syme->count[counter]++;<br />
			record_precise_ip(syme, counter, ip);<br />
			pthread_mutex_lock(&#038;active_symbols_lock);<br />
			if (list_empty(&#038;syme->node) || !syme->node.next)<br />
				__list_insert_active_sym(syme);<br />
			pthread_mutex_unlock(&#038;active_symbols_lock);<br />
			return;<br />
		}<br />
	}</p>
<p>	samples&#8211;;<br />
}</p>
<p>static void process_event(u64 ip, int counter, int user)<br />
{<br />
	samples++;</p>
<p>	if (user) {<br />
		userspace_samples++;<br />
		return;<br />
	}</p>
<p>	record_ip(ip, counter);<br />
}</p>
<p>struct mmap_data {<br />
	int			counter;<br />
	void			*base;<br />
	int			mask;<br />
	unsigned int		prev;<br />
};</p>
<p>static unsigned int mmap_read_head(struct mmap_data *md)<br />
{<br />
	struct perf_event_mmap_page *pc = md->base;<br />
	int head;</p>
<p>	head = pc->data_head;<br />
	rmb();</p>
<p>	return head;<br />
}</p>
<p>struct timeval last_read, this_read;</p>
<p>static void mmap_read_counter(struct mmap_data *md)<br />
{<br />
	unsigned int head = mmap_read_head(md);<br />
	unsigned int old = md->prev;<br />
	unsigned char *data = md->base + page_size;<br />
	int diff;</p>
<p>	gettimeofday(&#038;this_read, NULL);</p>
<p>	/*<br />
	 * If we&#8217;re further behind than half the buffer, there&#8217;s a chance<br />
	 * the writer will bite our tail and mess up the samples under us.<br />
	 *<br />
	 * If we somehow ended up ahead of the head, we got messed up.<br />
	 *<br />
	 * In either case, truncate and restart at head.<br />
	 */<br />
	diff = head &#8211; old;<br />
	if (diff > md->mask / 2 || diff < 0) {<br />
		struct timeval iv;<br />
		unsigned long msecs;</p>
<p>		timersub(&#038;this_read, &#038;last_read, &#038;iv);<br />
		msecs = iv.tv_sec*1000 + iv.tv_usec/1000;</p>
<p>		fprintf(stderr, "WARNING: failed to keep up with mmap data."<br />
				"  Last read %lu msecs ago.\n", msecs);</p>
<p>		/*<br />
		 * head points to a known good entry, start there.<br />
		 */<br />
		old = head;<br />
	}</p>
<p>	last_read = this_read;</p>
<p>	for (; old != head;) {<br />
		event_t *event = (event_t *)&#038;data[old &#038; md->mask];</p>
<p>		event_t event_copy;</p>
<p>		size_t size = event->header.size;</p>
<p>		/*<br />
		 * Event straddles the mmap boundary &#8212; header should always<br />
		 * be inside due to u64 alignment of output.<br />
		 */<br />
		if ((old &#038; md->mask) + size != ((old + size) &#038; md->mask)) {<br />
			unsigned int offset = old;<br />
			unsigned int len = min(sizeof(*event), size), cpy;<br />
			void *dst = &#038;event_copy;</p>
<p>			do {<br />
				cpy = min(md->mask + 1 &#8211; (offset &#038; md->mask), len);<br />
				memcpy(dst, &#038;data[offset &#038; md->mask], cpy);<br />
				offset += cpy;<br />
				dst += cpy;<br />
				len -= cpy;<br />
			} while (len);</p>
<p>			event = &#038;event_copy;<br />
		}</p>
<p>		old += size;</p>
<p>		if (event->header.type == PERF_RECORD_SAMPLE) {<br />
			int user =<br />
	(event->header.misc &#038; PERF_RECORD_MISC_CPUMODE_MASK) == PERF_RECORD_MISC_USER;<br />
			process_event(event->ip.ip, md->counter, user);<br />
		}<br />
	}</p>
<p>	md->prev = old;<br />
}</p>
<p>static struct pollfd event_array[MAX_NR_CPUS * MAX_COUNTERS];<br />
static struct mmap_data mmap_array[MAX_NR_CPUS][MAX_COUNTERS];</p>
<p>static void mmap_read(void)<br />
{<br />
	int i, counter;</p>
<p>	for (i = 0; i < nr_cpus; i++) {<br />
		for (counter = 0; counter < nr_counters; counter++)<br />
			mmap_read_counter(&#038;mmap_array[i][counter]);<br />
	}<br />
}</p>
<p>int nr_poll;<br />
int group_fd;</p>
<p>static void start_counter(int i, int counter)<br />
{<br />
	struct perf_event_attr *attr;<br />
	int cpu;</p>
<p>	cpu = profile_cpu;<br />
	if (target_pid == -1 &#038;&#038; profile_cpu == -1)<br />
		cpu = i;</p>
<p>	attr = attrs + counter;</p>
<p>	attr->sample_type	= PERF_SAMPLE_IP | PERF_SAMPLE_TID;<br />
	attr->freq		= freq;<br />
	attr->inherit		= (cpu < 0) &#038;& inherit;</p>
<p>try_again:<br />
	fd[i][counter] = sys_perf_event_open(attr, target_pid, cpu, group_fd, 0);</p>
<p>	if (fd[i][counter] < 0) {<br />
		int err = errno;</p>
<p>		if (err == EPERM || err == EACCES)<br />
			die("No permission - are you root?\n");<br />
		/*<br />
		 * If it's cycles then fall back to hrtimer<br />
		 * based cpu-clock-tick sw counter, which<br />
		 * is always available even if no PMU support:<br />
		 */<br />
		if (attr->type == PERF_TYPE_HARDWARE<br />
			&#038;&#038; attr->config == PERF_COUNT_HW_CPU_CYCLES) {</p>
<p>			if (verbose)<br />
				warning(&raquo; &#8230; trying to fall back to cpu-clock-ticks\n&raquo;);</p>
<p>			attr->type = PERF_TYPE_SOFTWARE;<br />
			attr->config = PERF_COUNT_SW_CPU_CLOCK;<br />
			goto try_again;<br />
		}<br />
		printf(&raquo;\n&raquo;);<br />
		error(&raquo;perfcounter syscall returned with %d (%s)\n&raquo;,<br />
			fd[i][counter], strerror(err));<br />
		die(&raquo;No CONFIG_PERF_EVENTS=y kernel support configured?\n&raquo;);<br />
		exit(-1);<br />
	}<br />
	assert(fd[i][counter] >= 0);<br />
	fcntl(fd[i][counter], F_SETFL, O_NONBLOCK);</p>
<p>	/*<br />
	 * First counter acts as the group leader:<br />
	 */<br />
	if (group &#038;&#038; group_fd == -1)<br />
		group_fd = fd[i][counter];</p>
<p>	event_array[nr_poll].fd = fd[i][counter];<br />
	event_array[nr_poll].events = POLLIN;<br />
	nr_poll++;</p>
<p>	mmap_array[i][counter].counter = counter;<br />
	mmap_array[i][counter].prev = 0;<br />
	mmap_array[i][counter].mask = mmap_pages*page_size &#8211; 1;<br />
	mmap_array[i][counter].base = mmap(NULL, (mmap_pages+1)*page_size,<br />
			PROT_READ, MAP_SHARED, fd[i][counter], 0);<br />
	if (mmap_array[i][counter].base == MAP_FAILED)<br />
		die(&raquo;failed to mmap with %d (%s)\n&raquo;, errno, strerror(errno));<br />
}</p>
<p>static int __cmd_top(void)<br />
{<br />
	pthread_t thread;<br />
	int i, counter;<br />
	int ret;</p>
<p>	for (i = 0; i < nr_cpus; i++) {<br />
		group_fd = -1;<br />
		for (counter = 0; counter < nr_counters; counter++)<br />
			start_counter(i, counter);<br />
	}</p>
<p>	/* Wait for a minimal set of events before starting the snapshot */<br />
	poll(event_array, nr_poll, 100);</p>
<p>	mmap_read();</p>
<p>	if (pthread_create(&#038;thread, NULL, display_thread, NULL)) {<br />
		printf("Could not create display thread.\n");<br />
		exit(-1);<br />
	}</p>
<p>	if (realtime_prio) {<br />
		struct sched_param param;</p>
<p>		param.sched_priority = realtime_prio;<br />
		if (sched_setscheduler(0, SCHED_FIFO, &#038;param)) {<br />
			printf("Could not set realtime priority.\n");<br />
			exit(-1);<br />
		}<br />
	}</p>
<p>	while (1) {<br />
		int hits = samples;</p>
<p>		mmap_read();</p>
<p>		if (hits == samples)<br />
			ret = poll(event_array, nr_poll, 100);<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static const char * const top_usage[] = {<br />
	"perf top [<options>]&raquo;,<br />
	NULL<br />
};</p>
<p>static const struct option options[] = {<br />
	OPT_CALLBACK(&#8217;e', &laquo;event&raquo;, NULL, &laquo;event&raquo;,<br />
		     &laquo;event selector. use &#8216;perf list&#8217; to list available events&raquo;,<br />
		     parse_events),<br />
	OPT_INTEGER(&#8217;c', &laquo;count&raquo;, &#038;default_interval,<br />
		    &laquo;event period to sample&raquo;),<br />
	OPT_INTEGER(&#8217;p', &laquo;pid&raquo;, &#038;target_pid,<br />
		    &laquo;profile events on existing pid&raquo;),<br />
	OPT_BOOLEAN(&#8217;a', &laquo;all-cpus&raquo;, &#038;system_wide,<br />
			    &laquo;system-wide collection from all CPUs&raquo;),<br />
	OPT_INTEGER(&#8217;C', &laquo;CPU&raquo;, &#038;profile_cpu,<br />
		    &laquo;CPU to profile on&raquo;),<br />
	OPT_STRING(&#8217;k', &laquo;vmlinux&raquo;, &#038;vmlinux_name, &laquo;file&raquo;, &laquo;vmlinux pathname&raquo;),<br />
	OPT_INTEGER(&#8217;m', &laquo;mmap-pages&raquo;, &#038;mmap_pages,<br />
		    &laquo;number of mmap data pages&raquo;),<br />
	OPT_INTEGER(&#8217;r', &laquo;realtime&raquo;, &#038;realtime_prio,<br />
		    &laquo;collect data with this RT SCHED_FIFO priority&raquo;),<br />
	OPT_INTEGER(&#8217;d', &laquo;delay&raquo;, &#038;delay_secs,<br />
		    &laquo;number of seconds to delay between refreshes&raquo;),<br />
	OPT_BOOLEAN(&#8217;D', &laquo;dump-symtab&raquo;, &#038;dump_symtab,<br />
			    &laquo;dump the symbol table used for profiling&raquo;),<br />
	OPT_INTEGER(&#8217;f', &laquo;count-filter&raquo;, &#038;count_filter,<br />
		    &laquo;only display functions with more events than this&raquo;),<br />
	OPT_BOOLEAN(&#8217;g', &laquo;group&raquo;, &#038;group,<br />
			    &laquo;put the counters into a counter group&raquo;),<br />
	OPT_BOOLEAN(&#8217;i', &laquo;inherit&raquo;, &#038;inherit,<br />
		    &laquo;child tasks inherit counters&raquo;),<br />
	OPT_STRING(&#8217;s&#8217;, &laquo;sym-annotate&raquo;, &#038;sym_filter, &laquo;symbol name&raquo;,<br />
		    &laquo;symbol to annotate &#8211; requires -k option&raquo;),<br />
	OPT_BOOLEAN(&#8217;z', &laquo;zero&raquo;, &#038;zero,<br />
		    &laquo;zero history across updates&raquo;),<br />
	OPT_INTEGER(&#8217;F', &laquo;freq&raquo;, &#038;freq,<br />
		    &laquo;profile at this frequency&raquo;),<br />
	OPT_INTEGER(&#8217;E', &laquo;entries&raquo;, &#038;print_entries,<br />
		    &laquo;display this many functions&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show counter open errors, etc)&raquo;),<br />
	OPT_END()<br />
};</p>
<p>int cmd_top(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	int counter;</p>
<p>	symbol__init();</p>
<p>	page_size = sysconf(_SC_PAGE_SIZE);</p>
<p>	argc = parse_options(argc, argv, options, top_usage, 0);<br />
	if (argc)<br />
		usage_with_options(top_usage, options);</p>
<p>	if (freq) {<br />
		default_interval = freq;<br />
		freq = 1;<br />
	}</p>
<p>	/* CPU and PID are mutually exclusive */<br />
	if (target_pid != -1 &#038;&#038; profile_cpu != -1) {<br />
		printf(&raquo;WARNING: PID switch overriding CPU\n&raquo;);<br />
		sleep(1);<br />
		profile_cpu = -1;<br />
	}</p>
<p>	if (!nr_counters)<br />
		nr_counters = 1;</p>
<p>	if (delay_secs < 1)<br />
		delay_secs = 1;</p>
<p>	parse_symbols();<br />
	parse_source(sym_filter_entry);</p>
<p>	/*<br />
	 * Fill in the ones not specifically initialized via -c:<br />
	 */<br />
	for (counter = 0; counter < nr_counters; counter++) {<br />
		if (attrs[counter].sample_period)<br />
			continue;</p>
<p>		attrs[counter].sample_period = default_interval;<br />
	}</p>
<p>	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);<br />
	assert(nr_cpus <= MAX_NR_CPUS);<br />
	assert(nr_cpus >= 0);</p>
<p>	if (target_pid != -1 || profile_cpu != -1)<br />
		nr_cpus = 1;</p>
<p>	return __cmd_top();<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-top-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-timechart.c</title>
		<link>http://lynyrd.ru/builtin-timechart-c</link>
		<comments>http://lynyrd.ru/builtin-timechart-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:28:22 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/builtin-timechart-c</guid>
		<description><![CDATA[/*
 * builtin-timechart.c &#8211; make an svg timechart of system activity
 *
 * (C) Copyright 2009 Intel Corporation
 *
 * Authors:
 *     Arjan van de Ven 
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-timechart.c &#8211; make an svg timechart of system activity<span id="more-608"></span><br />
 *<br />
 * (C) Copyright 2009 Intel Corporation<br />
 *<br />
 * Authors:<br />
 *     Arjan van de Ven <arjan@linux.intel.com><br />
 *<br />
 * This program is free software; you can redistribute it and/or<br />
 * modify it under the terms of the GNU General Public License<br />
 * as published by the Free Software Foundation; version 2<br />
 * of the License.<br />
 */</p>
<p>#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;util/util.h&raquo;</p>
<p>#include &laquo;util/color.h&raquo;<br />
#include
<linux/list.h>
#include &laquo;util/cache.h&raquo;<br />
#include
<linux/rbtree.h>
#include &laquo;util/symbol.h&raquo;<br />
#include &laquo;util/string.h&raquo;<br />
#include &laquo;util/callchain.h&raquo;<br />
#include &laquo;util/strlist.h&raquo;</p>
<p>#include &laquo;perf.h&raquo;<br />
#include &laquo;util/header.h&raquo;<br />
#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;<br />
#include &laquo;util/svghelper.h&raquo;</p>
<p>static char		const *input_name = &laquo;perf.data&raquo;;<br />
static char		const *output_name = &laquo;output.svg&raquo;;</p>
<p>static unsigned long	page_size;<br />
static unsigned long	mmap_window = 32;<br />
static u64		sample_type;</p>
<p>static unsigned int	numcpus;<br />
static u64		min_freq;	/* Lowest CPU frequency seen */<br />
static u64		max_freq;	/* Highest CPU frequency seen */<br />
static u64		turbo_frequency;</p>
<p>static u64		first_time, last_time;</p>
<p>static int		power_only;</p>
<p>static struct perf_header	*header;</p>
<p>struct per_pid;<br />
struct per_pidcomm;</p>
<p>struct cpu_sample;<br />
struct power_event;<br />
struct wake_event;</p>
<p>struct sample_wrapper;</p>
<p>/*<br />
 * Datastructure layout:<br />
 * We keep an list of &laquo;pid&raquo;s, matching the kernels notion of a task struct.<br />
 * Each &laquo;pid&raquo; entry, has a list of &laquo;comm&raquo;s.<br />
 *	this is because we want to track different programs different, while<br />
 *	exec will reuse the original pid (by design).<br />
 * Each comm has a list of samples that will be used to draw<br />
 * final graph.<br />
 */</p>
<p>struct per_pid {<br />
	struct per_pid *next;</p>
<p>	int		pid;<br />
	int		ppid;</p>
<p>	u64		start_time;<br />
	u64		end_time;<br />
	u64		total_time;<br />
	int		display;</p>
<p>	struct per_pidcomm *all;<br />
	struct per_pidcomm *current;</p>
<p>	int painted;<br />
};</p>
<p>struct per_pidcomm {<br />
	struct per_pidcomm *next;</p>
<p>	u64		start_time;<br />
	u64		end_time;<br />
	u64		total_time;</p>
<p>	int		Y;<br />
	int		display;</p>
<p>	long		state;<br />
	u64		state_since;</p>
<p>	char		*comm;</p>
<p>	struct cpu_sample *samples;<br />
};</p>
<p>struct sample_wrapper {<br />
	struct sample_wrapper *next;</p>
<p>	u64		timestamp;<br />
	unsigned char	data[0];<br />
};</p>
<p>#define TYPE_NONE	0<br />
#define TYPE_RUNNING	1<br />
#define TYPE_WAITING	2<br />
#define TYPE_BLOCKED	3</p>
<p>struct cpu_sample {<br />
	struct cpu_sample *next;</p>
<p>	u64 start_time;<br />
	u64 end_time;<br />
	int type;<br />
	int cpu;<br />
};</p>
<p>static struct per_pid *all_data;</p>
<p>#define CSTATE 1<br />
#define PSTATE 2</p>
<p>struct power_event {<br />
	struct power_event *next;<br />
	int type;<br />
	int state;<br />
	u64 start_time;<br />
	u64 end_time;<br />
	int cpu;<br />
};</p>
<p>struct wake_event {<br />
	struct wake_event *next;<br />
	int waker;<br />
	int wakee;<br />
	u64 time;<br />
};</p>
<p>static struct power_event    *power_events;<br />
static struct wake_event     *wake_events;</p>
<p>struct sample_wrapper *all_samples;</p>
<p>static struct per_pid *find_create_pid(int pid)<br />
{<br />
	struct per_pid *cursor = all_data;</p>
<p>	while (cursor) {<br />
		if (cursor->pid == pid)<br />
			return cursor;<br />
		cursor = cursor->next;<br />
	}<br />
	cursor = malloc(sizeof(struct per_pid));<br />
	assert(cursor != NULL);<br />
	memset(cursor, 0, sizeof(struct per_pid));<br />
	cursor->pid = pid;<br />
	cursor->next = all_data;<br />
	all_data = cursor;<br />
	return cursor;<br />
}</p>
<p>static void pid_set_comm(int pid, char *comm)<br />
{<br />
	struct per_pid *p;<br />
	struct per_pidcomm *c;<br />
	p = find_create_pid(pid);<br />
	c = p->all;<br />
	while (c) {<br />
		if (c->comm &#038;&#038; strcmp(c->comm, comm) == 0) {<br />
			p->current = c;<br />
			return;<br />
		}<br />
		if (!c->comm) {<br />
			c->comm = strdup(comm);<br />
			p->current = c;<br />
			return;<br />
		}<br />
		c = c->next;<br />
	}<br />
	c = malloc(sizeof(struct per_pidcomm));<br />
	assert(c != NULL);<br />
	memset(c, 0, sizeof(struct per_pidcomm));<br />
	c->comm = strdup(comm);<br />
	p->current = c;<br />
	c->next = p->all;<br />
	p->all = c;<br />
}</p>
<p>static void pid_fork(int pid, int ppid, u64 timestamp)<br />
{<br />
	struct per_pid *p, *pp;<br />
	p = find_create_pid(pid);<br />
	pp = find_create_pid(ppid);<br />
	p->ppid = ppid;<br />
	if (pp->current &#038;&#038; pp->current->comm &#038;&#038; !p->current)<br />
		pid_set_comm(pid, pp->current->comm);</p>
<p>	p->start_time = timestamp;<br />
	if (p->current) {<br />
		p->current->start_time = timestamp;<br />
		p->current->state_since = timestamp;<br />
	}<br />
}</p>
<p>static void pid_exit(int pid, u64 timestamp)<br />
{<br />
	struct per_pid *p;<br />
	p = find_create_pid(pid);<br />
	p->end_time = timestamp;<br />
	if (p->current)<br />
		p->current->end_time = timestamp;<br />
}</p>
<p>static void<br />
pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)<br />
{<br />
	struct per_pid *p;<br />
	struct per_pidcomm *c;<br />
	struct cpu_sample *sample;</p>
<p>	p = find_create_pid(pid);<br />
	c = p->current;<br />
	if (!c) {<br />
		c = malloc(sizeof(struct per_pidcomm));<br />
		assert(c != NULL);<br />
		memset(c, 0, sizeof(struct per_pidcomm));<br />
		p->current = c;<br />
		c->next = p->all;<br />
		p->all = c;<br />
	}</p>
<p>	sample = malloc(sizeof(struct cpu_sample));<br />
	assert(sample != NULL);<br />
	memset(sample, 0, sizeof(struct cpu_sample));<br />
	sample->start_time = start;<br />
	sample->end_time = end;<br />
	sample->type = type;<br />
	sample->next = c->samples;<br />
	sample->cpu = cpu;<br />
	c->samples = sample;</p>
<p>	if (sample->type == TYPE_RUNNING &#038;&#038; end > start &#038;&#038; start > 0) {<br />
		c->total_time += (end-start);<br />
		p->total_time += (end-start);<br />
	}</p>
<p>	if (c->start_time == 0 || c->start_time > start)<br />
		c->start_time = start;<br />
	if (p->start_time == 0 || p->start_time > start)<br />
		p->start_time = start;</p>
<p>	if (cpu > numcpus)<br />
		numcpus = cpu;<br />
}</p>
<p>#define MAX_CPUS 4096</p>
<p>static u64 cpus_cstate_start_times[MAX_CPUS];<br />
static int cpus_cstate_state[MAX_CPUS];<br />
static u64 cpus_pstate_start_times[MAX_CPUS];<br />
static u64 cpus_pstate_state[MAX_CPUS];</p>
<p>static int<br />
process_comm_event(event_t *event)<br />
{<br />
	pid_set_comm(event->comm.pid, event->comm.comm);<br />
	return 0;<br />
}<br />
static int<br />
process_fork_event(event_t *event)<br />
{<br />
	pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);<br />
	return 0;<br />
}</p>
<p>static int<br />
process_exit_event(event_t *event)<br />
{<br />
	pid_exit(event->fork.pid, event->fork.time);<br />
	return 0;<br />
}</p>
<p>struct trace_entry {<br />
	u32			size;<br />
	unsigned short		type;<br />
	unsigned char		flags;<br />
	unsigned char		preempt_count;<br />
	int			pid;<br />
	int			tgid;<br />
};</p>
<p>struct power_entry {<br />
	struct trace_entry te;<br />
	s64	type;<br />
	s64	value;<br />
};</p>
<p>#define TASK_COMM_LEN 16<br />
struct wakeup_entry {<br />
	struct trace_entry te;<br />
	char comm[TASK_COMM_LEN];<br />
	int   pid;<br />
	int   prio;<br />
	int   success;<br />
};</p>
<p>/*<br />
 * trace_flag_type is an enumeration that holds different<br />
 * states when a trace occurs. These are:<br />
 *  IRQS_OFF            &#8211; interrupts were disabled<br />
 *  IRQS_NOSUPPORT      &#8211; arch does not support irqs_disabled_flags<br />
 *  NEED_RESCED         &#8211; reschedule is requested<br />
 *  HARDIRQ             &#8211; inside an interrupt handler<br />
 *  SOFTIRQ             &#8211; inside a softirq handler<br />
 */<br />
enum trace_flag_type {<br />
	TRACE_FLAG_IRQS_OFF		= 0&#215;01,<br />
	TRACE_FLAG_IRQS_NOSUPPORT	= 0&#215;02,<br />
	TRACE_FLAG_NEED_RESCHED		= 0&#215;04,<br />
	TRACE_FLAG_HARDIRQ		= 0&#215;08,<br />
	TRACE_FLAG_SOFTIRQ		= 0&#215;10,<br />
};</p>
<p>struct sched_switch {<br />
	struct trace_entry te;<br />
	char prev_comm[TASK_COMM_LEN];<br />
	int  prev_pid;<br />
	int  prev_prio;<br />
	long prev_state; /* Arjan weeps. */<br />
	char next_comm[TASK_COMM_LEN];<br />
	int  next_pid;<br />
	int  next_prio;<br />
};</p>
<p>static void c_state_start(int cpu, u64 timestamp, int state)<br />
{<br />
	cpus_cstate_start_times[cpu] = timestamp;<br />
	cpus_cstate_state[cpu] = state;<br />
}</p>
<p>static void c_state_end(int cpu, u64 timestamp)<br />
{<br />
	struct power_event *pwr;<br />
	pwr = malloc(sizeof(struct power_event));<br />
	if (!pwr)<br />
		return;<br />
	memset(pwr, 0, sizeof(struct power_event));</p>
<p>	pwr->state = cpus_cstate_state[cpu];<br />
	pwr->start_time = cpus_cstate_start_times[cpu];<br />
	pwr->end_time = timestamp;<br />
	pwr->cpu = cpu;<br />
	pwr->type = CSTATE;<br />
	pwr->next = power_events;</p>
<p>	power_events = pwr;<br />
}</p>
<p>static void p_state_change(int cpu, u64 timestamp, u64 new_freq)<br />
{<br />
	struct power_event *pwr;<br />
	pwr = malloc(sizeof(struct power_event));</p>
<p>	if (new_freq > 8000000) /* detect invalid data */<br />
		return;</p>
<p>	if (!pwr)<br />
		return;<br />
	memset(pwr, 0, sizeof(struct power_event));</p>
<p>	pwr->state = cpus_pstate_state[cpu];<br />
	pwr->start_time = cpus_pstate_start_times[cpu];<br />
	pwr->end_time = timestamp;<br />
	pwr->cpu = cpu;<br />
	pwr->type = PSTATE;<br />
	pwr->next = power_events;</p>
<p>	if (!pwr->start_time)<br />
		pwr->start_time = first_time;</p>
<p>	power_events = pwr;</p>
<p>	cpus_pstate_state[cpu] = new_freq;<br />
	cpus_pstate_start_times[cpu] = timestamp;</p>
<p>	if ((u64)new_freq > max_freq)<br />
		max_freq = new_freq;</p>
<p>	if (new_freq < min_freq || min_freq == 0)<br />
		min_freq = new_freq;</p>
<p>	if (new_freq == max_freq - 1000)<br />
			turbo_frequency = max_freq;<br />
}</p>
<p>static void<br />
sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)<br />
{<br />
	struct wake_event *we;<br />
	struct per_pid *p;<br />
	struct wakeup_entry *wake = (void *)te;</p>
<p>	we = malloc(sizeof(struct wake_event));<br />
	if (!we)<br />
		return;</p>
<p>	memset(we, 0, sizeof(struct wake_event));<br />
	we->time = timestamp;<br />
	we->waker = pid;</p>
<p>	if ((te->flags &#038; TRACE_FLAG_HARDIRQ) || (te->flags &#038; TRACE_FLAG_SOFTIRQ))<br />
		we->waker = -1;</p>
<p>	we->wakee = wake->pid;<br />
	we->next = wake_events;<br />
	wake_events = we;<br />
	p = find_create_pid(we->wakee);</p>
<p>	if (p &#038;&#038; p->current &#038;&#038; p->current->state == TYPE_NONE) {<br />
		p->current->state_since = timestamp;<br />
		p->current->state = TYPE_WAITING;<br />
	}<br />
	if (p &#038;&#038; p->current &#038;&#038; p->current->state == TYPE_BLOCKED) {<br />
		pid_put_sample(p->pid, p->current->state, cpu, p->current->state_since, timestamp);<br />
		p->current->state_since = timestamp;<br />
		p->current->state = TYPE_WAITING;<br />
	}<br />
}</p>
<p>static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)<br />
{<br />
	struct per_pid *p = NULL, *prev_p;<br />
	struct sched_switch *sw = (void *)te;</p>
<p>	prev_p = find_create_pid(sw->prev_pid);</p>
<p>	p = find_create_pid(sw->next_pid);</p>
<p>	if (prev_p->current &#038;&#038; prev_p->current->state != TYPE_NONE)<br />
		pid_put_sample(sw->prev_pid, TYPE_RUNNING, cpu, prev_p->current->state_since, timestamp);<br />
	if (p &#038;&#038; p->current) {<br />
		if (p->current->state != TYPE_NONE)<br />
			pid_put_sample(sw->next_pid, p->current->state, cpu, p->current->state_since, timestamp);</p>
<p>			p->current->state_since = timestamp;<br />
			p->current->state = TYPE_RUNNING;<br />
	}</p>
<p>	if (prev_p->current) {<br />
		prev_p->current->state = TYPE_NONE;<br />
		prev_p->current->state_since = timestamp;<br />
		if (sw->prev_state &#038; 2)<br />
			prev_p->current->state = TYPE_BLOCKED;<br />
		if (sw->prev_state == 0)<br />
			prev_p->current->state = TYPE_WAITING;<br />
	}<br />
}</p>
<p>static int<br />
process_sample_event(event_t *event)<br />
{<br />
	int cursor = 0;<br />
	u64 addr = 0;<br />
	u64 stamp = 0;<br />
	u32 cpu = 0;<br />
	u32 pid = 0;<br />
	struct trace_entry *te;</p>
<p>	if (sample_type &#038; PERF_SAMPLE_IP)<br />
		cursor++;</p>
<p>	if (sample_type &#038; PERF_SAMPLE_TID) {<br />
		pid = event->sample.array[cursor]>>32;<br />
		cursor++;<br />
	}<br />
	if (sample_type &#038; PERF_SAMPLE_TIME) {<br />
		stamp = event->sample.array[cursor++];</p>
<p>		if (!first_time || first_time > stamp)<br />
			first_time = stamp;<br />
		if (last_time < stamp)<br />
			last_time = stamp;</p>
<p>	}<br />
	if (sample_type &#038; PERF_SAMPLE_ADDR)<br />
		addr = event->sample.array[cursor++];<br />
	if (sample_type &#038; PERF_SAMPLE_ID)<br />
		cursor++;<br />
	if (sample_type &#038; PERF_SAMPLE_STREAM_ID)<br />
		cursor++;<br />
	if (sample_type &#038; PERF_SAMPLE_CPU)<br />
		cpu = event->sample.array[cursor++] &#038; 0xFFFFFFFF;<br />
	if (sample_type &#038; PERF_SAMPLE_PERIOD)<br />
		cursor++;</p>
<p>	te = (void *)&#038;event->sample.array[cursor];</p>
<p>	if (sample_type &#038; PERF_SAMPLE_RAW &#038;&#038; te->size > 0) {<br />
		char *event_str;<br />
		struct power_entry *pe;</p>
<p>		pe = (void *)te;</p>
<p>		event_str = perf_header__find_event(te->type);</p>
<p>		if (!event_str)<br />
			return 0;</p>
<p>		if (strcmp(event_str, &laquo;power:power_start&raquo;) == 0)<br />
			c_state_start(cpu, stamp, pe->value);</p>
<p>		if (strcmp(event_str, &laquo;power:power_end&raquo;) == 0)<br />
			c_state_end(cpu, stamp);</p>
<p>		if (strcmp(event_str, &laquo;power:power_frequency&raquo;) == 0)<br />
			p_state_change(cpu, stamp, pe->value);</p>
<p>		if (strcmp(event_str, &laquo;sched:sched_wakeup&raquo;) == 0)<br />
			sched_wakeup(cpu, stamp, pid, te);</p>
<p>		if (strcmp(event_str, &laquo;sched:sched_switch&raquo;) == 0)<br />
			sched_switch(cpu, stamp, te);<br />
	}<br />
	return 0;<br />
}</p>
<p>/*<br />
 * After the last sample we need to wrap up the current C/P state<br />
 * and close out each CPU for these.<br />
 */<br />
static void end_sample_processing(void)<br />
{<br />
	u64 cpu;<br />
	struct power_event *pwr;</p>
<p>	for (cpu = 0; cpu <= numcpus; cpu++) {<br />
		pwr = malloc(sizeof(struct power_event));<br />
		if (!pwr)<br />
			return;<br />
		memset(pwr, 0, sizeof(struct power_event));</p>
<p>		/* C state */<br />
#if 0<br />
		pwr->state = cpus_cstate_state[cpu];<br />
		pwr->start_time = cpus_cstate_start_times[cpu];<br />
		pwr->end_time = last_time;<br />
		pwr->cpu = cpu;<br />
		pwr->type = CSTATE;<br />
		pwr->next = power_events;</p>
<p>		power_events = pwr;<br />
#endif<br />
		/* P state */</p>
<p>		pwr = malloc(sizeof(struct power_event));<br />
		if (!pwr)<br />
			return;<br />
		memset(pwr, 0, sizeof(struct power_event));</p>
<p>		pwr->state = cpus_pstate_state[cpu];<br />
		pwr->start_time = cpus_pstate_start_times[cpu];<br />
		pwr->end_time = last_time;<br />
		pwr->cpu = cpu;<br />
		pwr->type = PSTATE;<br />
		pwr->next = power_events;</p>
<p>		if (!pwr->start_time)<br />
			pwr->start_time = first_time;<br />
		if (!pwr->state)<br />
			pwr->state = min_freq;<br />
		power_events = pwr;<br />
	}<br />
}</p>
<p>static u64 sample_time(event_t *event)<br />
{<br />
	int cursor;</p>
<p>	cursor = 0;<br />
	if (sample_type &#038; PERF_SAMPLE_IP)<br />
		cursor++;<br />
	if (sample_type &#038; PERF_SAMPLE_TID)<br />
		cursor++;<br />
	if (sample_type &#038; PERF_SAMPLE_TIME)<br />
		return event->sample.array[cursor];<br />
	return 0;<br />
}</p>
<p>/*<br />
 * We first queue all events, sorted backwards by insertion.<br />
 * The order will get flipped later.<br />
 */<br />
static int<br />
queue_sample_event(event_t *event)<br />
{<br />
	struct sample_wrapper *copy, *prev;<br />
	int size;</p>
<p>	size = event->sample.header.size + sizeof(struct sample_wrapper) + 8;</p>
<p>	copy = malloc(size);<br />
	if (!copy)<br />
		return 1;</p>
<p>	memset(copy, 0, size);</p>
<p>	copy->next = NULL;<br />
	copy->timestamp = sample_time(event);</p>
<p>	memcpy(&#038;copy->data, event, event->sample.header.size);</p>
<p>	/* insert in the right place in the list */</p>
<p>	if (!all_samples) {<br />
		/* first sample ever */<br />
		all_samples = copy;<br />
		return 0;<br />
	}</p>
<p>	if (all_samples->timestamp < copy->timestamp) {<br />
		/* insert at the head of the list */<br />
		copy->next = all_samples;<br />
		all_samples = copy;<br />
		return 0;<br />
	}</p>
<p>	prev = all_samples;<br />
	while (prev->next) {<br />
		if (prev->next->timestamp < copy->timestamp) {<br />
			copy->next = prev->next;<br />
			prev->next = copy;<br />
			return 0;<br />
		}<br />
		prev = prev->next;<br />
	}<br />
	/* insert at the end of the list */<br />
	prev->next = copy;</p>
<p>	return 0;<br />
}</p>
<p>static void sort_queued_samples(void)<br />
{<br />
	struct sample_wrapper *cursor, *next;</p>
<p>	cursor = all_samples;<br />
	all_samples = NULL;</p>
<p>	while (cursor) {<br />
		next = cursor->next;<br />
		cursor->next = all_samples;<br />
		all_samples = cursor;<br />
		cursor = next;<br />
	}<br />
}</p>
<p>/*<br />
 * Sort the pid datastructure<br />
 */<br />
static void sort_pids(void)<br />
{<br />
	struct per_pid *new_list, *p, *cursor, *prev;<br />
	/* sort by ppid first, then by pid, lowest to highest */</p>
<p>	new_list = NULL;</p>
<p>	while (all_data) {<br />
		p = all_data;<br />
		all_data = p->next;<br />
		p->next = NULL;</p>
<p>		if (new_list == NULL) {<br />
			new_list = p;<br />
			p->next = NULL;<br />
			continue;<br />
		}<br />
		prev = NULL;<br />
		cursor = new_list;<br />
		while (cursor) {<br />
			if (cursor->ppid > p->ppid ||<br />
				(cursor->ppid == p->ppid &#038;&#038; cursor->pid > p->pid)) {<br />
				/* must insert before */<br />
				if (prev) {<br />
					p->next = prev->next;<br />
					prev->next = p;<br />
					cursor = NULL;<br />
					continue;<br />
				} else {<br />
					p->next = new_list;<br />
					new_list = p;<br />
					cursor = NULL;<br />
					continue;<br />
				}<br />
			}</p>
<p>			prev = cursor;<br />
			cursor = cursor->next;<br />
			if (!cursor)<br />
				prev->next = p;<br />
		}<br />
	}<br />
	all_data = new_list;<br />
}</p>
<p>static void draw_c_p_states(void)<br />
{<br />
	struct power_event *pwr;<br />
	pwr = power_events;</p>
<p>	/*<br />
	 * two pass drawing so that the P state bars are on top of the C state blocks<br />
	 */<br />
	while (pwr) {<br />
		if (pwr->type == CSTATE)<br />
			svg_cstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);<br />
		pwr = pwr->next;<br />
	}</p>
<p>	pwr = power_events;<br />
	while (pwr) {<br />
		if (pwr->type == PSTATE) {<br />
			if (!pwr->state)<br />
				pwr->state = min_freq;<br />
			svg_pstate(pwr->cpu, pwr->start_time, pwr->end_time, pwr->state);<br />
		}<br />
		pwr = pwr->next;<br />
	}<br />
}</p>
<p>static void draw_wakeups(void)<br />
{<br />
	struct wake_event *we;<br />
	struct per_pid *p;<br />
	struct per_pidcomm *c;</p>
<p>	we = wake_events;<br />
	while (we) {<br />
		int from = 0, to = 0;<br />
		char *task_from = NULL, *task_to = NULL;</p>
<p>		/* locate the column of the waker and wakee */<br />
		p = all_data;<br />
		while (p) {<br />
			if (p->pid == we->waker || p->pid == we->wakee) {<br />
				c = p->all;<br />
				while (c) {<br />
					if (c->Y &#038;&#038; c->start_time <= we->time &#038;&#038; c->end_time >= we->time) {<br />
						if (p->pid == we->waker) {<br />
							from = c->Y;<br />
							task_from = strdup(c->comm);<br />
						}<br />
						if (p->pid == we->wakee) {<br />
							to = c->Y;<br />
							task_to = strdup(c->comm);<br />
						}<br />
					}<br />
					c = c->next;<br />
				}<br />
				c = p->all;<br />
				while (c) {<br />
					if (p->pid == we->waker &#038;&#038; !from) {<br />
						from = c->Y;<br />
						task_from = strdup(c->comm);<br />
					}<br />
					if (p->pid == we->wakee &#038;&#038; !to) {<br />
						to = c->Y;<br />
						task_to = strdup(c->comm);<br />
					}<br />
					c = c->next;<br />
				}<br />
			}<br />
			p = p->next;<br />
		}</p>
<p>		if (!task_from) {<br />
			task_from = malloc(40);<br />
			sprintf(task_from, &laquo;[%i]&laquo;, we->waker);<br />
		}<br />
		if (!task_to) {<br />
			task_to = malloc(40);<br />
			sprintf(task_to, &laquo;[%i]&laquo;, we->wakee);<br />
		}</p>
<p>		if (we->waker == -1)<br />
			svg_interrupt(we->time, to);<br />
		else if (from &#038;&#038; to &#038;&#038; abs(from &#8211; to) == 1)<br />
			svg_wakeline(we->time, from, to);<br />
		else<br />
			svg_partial_wakeline(we->time, from, task_from, to, task_to);<br />
		we = we->next;</p>
<p>		free(task_from);<br />
		free(task_to);<br />
	}<br />
}</p>
<p>static void draw_cpu_usage(void)<br />
{<br />
	struct per_pid *p;<br />
	struct per_pidcomm *c;<br />
	struct cpu_sample *sample;<br />
	p = all_data;<br />
	while (p) {<br />
		c = p->all;<br />
		while (c) {<br />
			sample = c->samples;<br />
			while (sample) {<br />
				if (sample->type == TYPE_RUNNING)<br />
					svg_process(sample->cpu, sample->start_time, sample->end_time, &laquo;sample&raquo;, c->comm);</p>
<p>				sample = sample->next;<br />
			}<br />
			c = c->next;<br />
		}<br />
		p = p->next;<br />
	}<br />
}</p>
<p>static void draw_process_bars(void)<br />
{<br />
	struct per_pid *p;<br />
	struct per_pidcomm *c;<br />
	struct cpu_sample *sample;<br />
	int Y = 0;</p>
<p>	Y = 2 * numcpus + 2;</p>
<p>	p = all_data;<br />
	while (p) {<br />
		c = p->all;<br />
		while (c) {<br />
			if (!c->display) {<br />
				c->Y = 0;<br />
				c = c->next;<br />
				continue;<br />
			}</p>
<p>			svg_box(Y, c->start_time, c->end_time, &laquo;process&raquo;);<br />
			sample = c->samples;<br />
			while (sample) {<br />
				if (sample->type == TYPE_RUNNING)<br />
					svg_sample(Y, sample->cpu, sample->start_time, sample->end_time);<br />
				if (sample->type == TYPE_BLOCKED)<br />
					svg_box(Y, sample->start_time, sample->end_time, &laquo;blocked&raquo;);<br />
				if (sample->type == TYPE_WAITING)<br />
					svg_waiting(Y, sample->start_time, sample->end_time);<br />
				sample = sample->next;<br />
			}</p>
<p>			if (c->comm) {<br />
				char comm[256];<br />
				if (c->total_time > 5000000000) /* 5 seconds */<br />
					sprintf(comm, &laquo;%s:%i (%2.2fs)&raquo;, c->comm, p->pid, c->total_time / 1000000000.0);<br />
				else<br />
					sprintf(comm, &laquo;%s:%i (%3.1fms)&raquo;, c->comm, p->pid, c->total_time / 1000000.0);</p>
<p>				svg_text(Y, c->start_time, comm);<br />
			}<br />
			c->Y = Y;<br />
			Y++;<br />
			c = c->next;<br />
		}<br />
		p = p->next;<br />
	}<br />
}</p>
<p>static int determine_display_tasks(u64 threshold)<br />
{<br />
	struct per_pid *p;<br />
	struct per_pidcomm *c;<br />
	int count = 0;</p>
<p>	p = all_data;<br />
	while (p) {<br />
		p->display = 0;<br />
		if (p->start_time == 1)<br />
			p->start_time = first_time;</p>
<p>		/* no exit marker, task kept running to the end */<br />
		if (p->end_time == 0)<br />
			p->end_time = last_time;<br />
		if (p->total_time >= threshold &#038;&#038; !power_only)<br />
			p->display = 1;</p>
<p>		c = p->all;</p>
<p>		while (c) {<br />
			c->display = 0;</p>
<p>			if (c->start_time == 1)<br />
				c->start_time = first_time;</p>
<p>			if (c->total_time >= threshold &#038;&#038; !power_only) {<br />
				c->display = 1;<br />
				count++;<br />
			}</p>
<p>			if (c->end_time == 0)<br />
				c->end_time = last_time;</p>
<p>			c = c->next;<br />
		}<br />
		p = p->next;<br />
	}<br />
	return count;<br />
}</p>
<p>#define TIME_THRESH 10000000</p>
<p>static void write_svg_file(const char *filename)<br />
{<br />
	u64 i;<br />
	int count;</p>
<p>	numcpus++;</p>
<p>	count = determine_display_tasks(TIME_THRESH);</p>
<p>	/* We&#8217;d like to show at least 15 tasks; be less picky if we have fewer */<br />
	if (count < 15)<br />
		count = determine_display_tasks(TIME_THRESH / 10);</p>
<p>	open_svg(filename, numcpus, count, first_time, last_time);</p>
<p>	svg_time_grid();<br />
	svg_legenda();</p>
<p>	for (i = 0; i < numcpus; i++)<br />
		svg_cpu_box(i, max_freq, turbo_frequency);</p>
<p>	draw_cpu_usage();<br />
	draw_process_bars();<br />
	draw_c_p_states();<br />
	draw_wakeups();</p>
<p>	svg_close();<br />
}</p>
<p>static int<br />
process_event(event_t *event)<br />
{</p>
<p>	switch (event->header.type) {</p>
<p>	case PERF_RECORD_COMM:<br />
		return process_comm_event(event);<br />
	case PERF_RECORD_FORK:<br />
		return process_fork_event(event);<br />
	case PERF_RECORD_EXIT:<br />
		return process_exit_event(event);<br />
	case PERF_RECORD_SAMPLE:<br />
		return queue_sample_event(event);</p>
<p>	/*<br />
	 * We dont process them right now but they are fine:<br />
	 */<br />
	case PERF_RECORD_MMAP:<br />
	case PERF_RECORD_THROTTLE:<br />
	case PERF_RECORD_UNTHROTTLE:<br />
		return 0;</p>
<p>	default:<br />
		return -1;<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static void process_samples(void)<br />
{<br />
	struct sample_wrapper *cursor;<br />
	event_t *event;</p>
<p>	sort_queued_samples();</p>
<p>	cursor = all_samples;<br />
	while (cursor) {<br />
		event = (void *)&#038;cursor->data;<br />
		cursor = cursor->next;<br />
		process_sample_event(event);<br />
	}<br />
}</p>
<p>static int __cmd_timechart(void)<br />
{<br />
	int ret, rc = EXIT_FAILURE;<br />
	unsigned long offset = 0;<br />
	unsigned long head, shift;<br />
	struct stat statbuf;<br />
	event_t *event;<br />
	uint32_t size;<br />
	char *buf;<br />
	int input;</p>
<p>	input = open(input_name, O_RDONLY);<br />
	if (input < 0) {<br />
		fprintf(stderr, " failed to open file: %s", input_name);<br />
		if (!strcmp(input_name, "perf.data"))<br />
			fprintf(stderr, "  (try 'perf record' first)");<br />
		fprintf(stderr, "\n");<br />
		exit(-1);<br />
	}</p>
<p>	ret = fstat(input, &#038;statbuf);<br />
	if (ret < 0) {<br />
		perror("failed to stat file");<br />
		exit(-1);<br />
	}</p>
<p>	if (!statbuf.st_size) {<br />
		fprintf(stderr, "zero-sized file, nothing to do!\n");<br />
		exit(0);<br />
	}</p>
<p>	header = perf_header__read(input);<br />
	head = header->data_offset;</p>
<p>	sample_type = perf_header__sample_type(header);</p>
<p>	shift = page_size * (head / page_size);<br />
	offset += shift;<br />
	head -= shift;</p>
<p>remap:<br />
	buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,<br />
			   MAP_SHARED, input, offset);<br />
	if (buf == MAP_FAILED) {<br />
		perror(&raquo;failed to mmap file&raquo;);<br />
		exit(-1);<br />
	}</p>
<p>more:<br />
	event = (event_t *)(buf + head);</p>
<p>	size = event->header.size;<br />
	if (!size)<br />
		size = 8;</p>
<p>	if (head + event->header.size >= page_size * mmap_window) {<br />
		int ret2;</p>
<p>		shift = page_size * (head / page_size);</p>
<p>		ret2 = munmap(buf, page_size * mmap_window);<br />
		assert(ret2 == 0);</p>
<p>		offset += shift;<br />
		head -= shift;<br />
		goto remap;<br />
	}</p>
<p>	size = event->header.size;</p>
<p>	if (!size || process_event(event) < 0) {</p>
<p>		printf("%p [%p]: skipping unknown header type: %d\n",<br />
			(void *)(offset + head),<br />
			(void *)(long)(event->header.size),<br />
			event->header.type);</p>
<p>		/*<br />
		 * assume we lost track of the stream, check alignment, and<br />
		 * increment a single u64 in the hope to catch on again &#8217;soon&#8217;.<br />
		 */</p>
<p>		if (unlikely(head &#038; 7))<br />
			head &#038;= ~7ULL;</p>
<p>		size = 8;<br />
	}</p>
<p>	head += size;</p>
<p>	if (offset + head >= header->data_offset + header->data_size)<br />
		goto done;</p>
<p>	if (offset + head < (unsigned long)statbuf.st_size)<br />
		goto more;</p>
<p>done:<br />
	rc = EXIT_SUCCESS;<br />
	close(input);</p>
<p>	process_samples();</p>
<p>	end_sample_processing();</p>
<p>	sort_pids();</p>
<p>	write_svg_file(output_name);</p>
<p>	printf("Written %2.1f seconds of trace to %s.\n", (last_time - first_time) / 1000000000.0, output_name);</p>
<p>	return rc;<br />
}</p>
<p>static const char * const timechart_usage[] = {<br />
	"perf timechart [<options>] {record}&raquo;,<br />
	NULL<br />
};</p>
<p>static const char *record_args[] = {<br />
	&laquo;record&raquo;,<br />
	&laquo;-a&raquo;,<br />
	&laquo;-R&raquo;,<br />
	&laquo;-M&raquo;,<br />
	&laquo;-f&raquo;,<br />
	&laquo;-c&raquo;, &laquo;1&#8243;,<br />
	&laquo;-e&raquo;, &laquo;power:power_start&raquo;,<br />
	&laquo;-e&raquo;, &laquo;power:power_end&raquo;,<br />
	&laquo;-e&raquo;, &laquo;power:power_frequency&raquo;,<br />
	&laquo;-e&raquo;, &laquo;sched:sched_wakeup&raquo;,<br />
	&laquo;-e&raquo;, &laquo;sched:sched_switch&raquo;,<br />
};</p>
<p>static int __cmd_record(int argc, const char **argv)<br />
{<br />
	unsigned int rec_argc, i, j;<br />
	const char **rec_argv;</p>
<p>	rec_argc = ARRAY_SIZE(record_args) + argc &#8211; 1;<br />
	rec_argv = calloc(rec_argc + 1, sizeof(char *));</p>
<p>	for (i = 0; i < ARRAY_SIZE(record_args); i++)<br />
		rec_argv[i] = strdup(record_args[i]);</p>
<p>	for (j = 1; j < (unsigned int)argc; j++, i++)<br />
		rec_argv[i] = argv[j];</p>
<p>	return cmd_record(i, rec_argv, NULL);<br />
}</p>
<p>static const struct option options[] = {<br />
	OPT_STRING(&#8217;i', &laquo;input&raquo;, &#038;input_name, &laquo;file&raquo;,<br />
		    &laquo;input file name&raquo;),<br />
	OPT_STRING(&#8217;o', &laquo;output&raquo;, &#038;output_name, &laquo;file&raquo;,<br />
		    &laquo;output file name&raquo;),<br />
	OPT_INTEGER(&#8217;w', &laquo;width&raquo;, &#038;svg_page_width,<br />
		    &laquo;page width&raquo;),<br />
	OPT_BOOLEAN(&#8217;p', &laquo;power-only&raquo;, &#038;power_only,<br />
		    &laquo;output power data only&raquo;),<br />
	OPT_END()<br />
};</p>
<p>int cmd_timechart(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	symbol__init();</p>
<p>	page_size = getpagesize();</p>
<p>	argc = parse_options(argc, argv, options, timechart_usage,<br />
			PARSE_OPT_STOP_AT_NON_OPTION);</p>
<p>	if (argc &#038;&#038; !strncmp(argv[0], &laquo;rec&raquo;, 3))<br />
		return __cmd_record(argc, argv);<br />
	else if (argc)<br />
		usage_with_options(timechart_usage, options);</p>
<p>	setup_pager();</p>
<p>	return __cmd_timechart();<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-timechart-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-stat.c</title>
		<link>http://lynyrd.ru/builtin-stat-c</link>
		<comments>http://lynyrd.ru/builtin-stat-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:27:57 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/builtin-stat-c</guid>
		<description><![CDATA[/*
 * builtin-stat.c
 *
 * Builtin stat command: Give a precise performance counters summary
 * overview about any workload, CPU or specific PID.
 *
 * Sample output:
   $ perf stat ~/hackbench 10
   Time: 0.104
    Performance counter stats for &#8216;/home/mingo/hackbench&#8217;:
       1255.538611  task ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-stat.c<span id="more-607"></span><br />
 *<br />
 * Builtin stat command: Give a precise performance counters summary<br />
 * overview about any workload, CPU or specific PID.<br />
 *<br />
 * Sample output:</p>
<p>   $ perf stat ~/hackbench 10<br />
   Time: 0.104</p>
<p>    Performance counter stats for &#8216;/home/mingo/hackbench&#8217;:</p>
<p>       1255.538611  task clock ticks     #      10.143 CPU utilization factor<br />
             54011  context switches     #       0.043 M/sec<br />
               385  CPU migrations       #       0.000 M/sec<br />
             17755  pagefaults           #       0.014 M/sec<br />
        3808323185  CPU cycles           #    3033.219 M/sec<br />
        1575111190  instructions         #    1254.530 M/sec<br />
          17367895  cache references     #      13.833 M/sec<br />
           7674421  cache misses         #       6.112 M/sec</p>
<p>    Wall-clock time elapsed:   123.786620 msecs</p>
<p> *<br />
 * Copyright (C) 2008, Red Hat Inc, Ingo Molnar <mingo@redhat.com><br />
 *<br />
 * Improvements and fixes by:<br />
 *<br />
 *   Arjan van de Ven <arjan@linux.intel.com><br />
 *   Yanmin Zhang <yanmin.zhang@intel.com><br />
 *   Wu Fengguang <fengguang.wu@intel.com><br />
 *   Mike Galbraith <efault@gmx.de><br />
 *   Paul Mackerras
<paulus@samba.org>
 *   Jaswinder Singh Rajput <jaswinder@kernel.org><br />
 *<br />
 * Released under the GPL v2. (and only v2, not any later version)<br />
 */</p>
<p>#include &laquo;perf.h&raquo;<br />
#include &laquo;builtin.h&raquo;<br />
#include &laquo;util/util.h&raquo;<br />
#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;<br />
#include &laquo;util/event.h&raquo;<br />
#include &laquo;util/debug.h&raquo;</p>
<p>#include <sys/prctl.h><br />
#include<br />
<math.h>
<p>static struct perf_event_attr default_attrs[] = {</p>
<p>  { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK	},<br />
  { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES},<br />
  { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CPU_MIGRATIONS	},<br />
  { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS	},</p>
<p>  { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES	},<br />
  { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS	},<br />
  { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_REFERENCES},<br />
  { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CACHE_MISSES	},</p>
<p>};</p>
<p>static int			system_wide			=  0;<br />
static unsigned int		nr_cpus				=  0;<br />
static int			run_idx				=  0;</p>
<p>static int			run_count			=  1;<br />
static int			inherit				=  1;<br />
static int			scale				=  1;<br />
static pid_t			target_pid			= -1;<br />
static pid_t			child_pid			= -1;<br />
static int			null_run			=  0;</p>
<p>static int			fd[MAX_NR_CPUS][MAX_COUNTERS];</p>
<p>static int			event_scaled[MAX_COUNTERS];</p>
<p>struct stats<br />
{<br />
	double n, mean, M2;<br />
};</p>
<p>static void update_stats(struct stats *stats, u64 val)<br />
{<br />
	double delta;</p>
<p>	stats->n++;<br />
	delta = val &#8211; stats->mean;<br />
	stats->mean += delta / stats->n;<br />
	stats->M2 += delta*(val &#8211; stats->mean);<br />
}</p>
<p>static double avg_stats(struct stats *stats)<br />
{<br />
	return stats->mean;<br />
}</p>
<p>/*<br />
 * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance<br />
 *<br />
 *       (\Sum n_i^2) &#8211; ((\Sum n_i)^2)/n<br />
 * s^2 = &#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
 *                  n &#8211; 1<br />
 *<br />
 * http://en.wikipedia.org/wiki/Stddev<br />
 *<br />
 * The std dev of the mean is related to the std dev by:<br />
 *<br />
 *             s<br />
 * s_mean = &#8212;&#8212;-<br />
 *          sqrt(n)<br />
 *<br />
 */<br />
static double stddev_stats(struct stats *stats)<br />
{<br />
	double variance = stats->M2 / (stats->n &#8211; 1);<br />
	double variance_mean = variance / stats->n;</p>
<p>	return sqrt(variance_mean);<br />
}</p>
<p>struct stats			event_res_stats[MAX_COUNTERS][3];<br />
struct stats			runtime_nsecs_stats;<br />
struct stats			walltime_nsecs_stats;<br />
struct stats			runtime_cycles_stats;</p>
<p>#define MATCH_EVENT(t, c, counter)			\<br />
	(attrs[counter].type == PERF_TYPE_##t &#038;&#038;	\<br />
	 attrs[counter].config == PERF_COUNT_##c)</p>
<p>#define ERR_PERF_OPEN \<br />
&laquo;Error: counter %d, sys_perf_event_open() syscall returned with %d (%s)\n&raquo;</p>
<p>static void create_perf_stat_counter(int counter, int pid)<br />
{<br />
	struct perf_event_attr *attr = attrs + counter;</p>
<p>	if (scale)<br />
		attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |<br />
				    PERF_FORMAT_TOTAL_TIME_RUNNING;</p>
<p>	if (system_wide) {<br />
		unsigned int cpu;</p>
<p>		for (cpu = 0; cpu < nr_cpus; cpu++) {<br />
			fd[cpu][counter] = sys_perf_event_open(attr, -1, cpu, -1, 0);<br />
			if (fd[cpu][counter] < 0 &#038;&#038; verbose)<br />
				fprintf(stderr, ERR_PERF_OPEN, counter,<br />
					fd[cpu][counter], strerror(errno));<br />
		}<br />
	} else {<br />
		attr->inherit	     = inherit;<br />
		attr->disabled	     = 1;<br />
		attr->enable_on_exec = 1;</p>
<p>		fd[0][counter] = sys_perf_event_open(attr, pid, -1, -1, 0);<br />
		if (fd[0][counter] < 0 &#038;&#038; verbose)<br />
			fprintf(stderr, ERR_PERF_OPEN, counter,<br />
				fd[0][counter], strerror(errno));<br />
	}<br />
}</p>
<p>/*<br />
 * Does the counter have nsecs as a unit?<br />
 */<br />
static inline int nsec_counter(int counter)<br />
{<br />
	if (MATCH_EVENT(SOFTWARE, SW_CPU_CLOCK, counter) ||<br />
	    MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>/*<br />
 * Read out the results of a single counter:<br />
 */<br />
static void read_counter(int counter)<br />
{<br />
	u64 count[3], single_count[3];<br />
	unsigned int cpu;<br />
	size_t res, nv;<br />
	int scaled;<br />
	int i;</p>
<p>	count[0] = count[1] = count[2] = 0;</p>
<p>	nv = scale ? 3 : 1;<br />
	for (cpu = 0; cpu < nr_cpus; cpu++) {<br />
		if (fd[cpu][counter] < 0)<br />
			continue;</p>
<p>		res = read(fd[cpu][counter], single_count, nv * sizeof(u64));<br />
		assert(res == nv * sizeof(u64));</p>
<p>		close(fd[cpu][counter]);<br />
		fd[cpu][counter] = -1;</p>
<p>		count[0] += single_count[0];<br />
		if (scale) {<br />
			count[1] += single_count[1];<br />
			count[2] += single_count[2];<br />
		}<br />
	}</p>
<p>	scaled = 0;<br />
	if (scale) {<br />
		if (count[2] == 0) {<br />
			event_scaled[counter] = -1;<br />
			count[0] = 0;<br />
			return;<br />
		}</p>
<p>		if (count[2] < count[1]) {<br />
			event_scaled[counter] = 1;<br />
			count[0] = (unsigned long long)<br />
				((double)count[0] * count[1] / count[2] + 0.5);<br />
		}<br />
	}</p>
<p>	for (i = 0; i < 3; i++)<br />
		update_stats(&#038;event_res_stats[counter][i], count[i]);</p>
<p>	if (verbose) {<br />
		fprintf(stderr, "%s: %Ld %Ld %Ld\n", event_name(counter),<br />
				count[0], count[1], count[2]);<br />
	}</p>
<p>	/*<br />
	 * Save the full runtime - to allow normalization during printout:<br />
	 */<br />
	if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter))<br />
		update_stats(&#038;runtime_nsecs_stats, count[0]);<br />
	if (MATCH_EVENT(HARDWARE, HW_CPU_CYCLES, counter))<br />
		update_stats(&#038;runtime_cycles_stats, count[0]);<br />
}</p>
<p>static int run_perf_stat(int argc __used, const char **argv)<br />
{<br />
	unsigned long long t0, t1;<br />
	int status = 0;<br />
	int counter;<br />
	int pid;<br />
	int child_ready_pipe[2], go_pipe[2];<br />
	char buf;</p>
<p>	if (!system_wide)<br />
		nr_cpus = 1;</p>
<p>	if (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0) {<br />
		perror("failed to create pipes");<br />
		exit(1);<br />
	}</p>
<p>	if ((pid = fork()) < 0)<br />
		perror("failed to fork");</p>
<p>	if (!pid) {<br />
		close(child_ready_pipe[0]);<br />
		close(go_pipe[1]);<br />
		fcntl(go_pipe[0], F_SETFD, FD_CLOEXEC);</p>
<p>		/*<br />
		 * Do a dummy execvp to get the PLT entry resolved,<br />
		 * so we avoid the resolver overhead on the real<br />
		 * execvp call.<br />
		 */<br />
		execvp("", (char **)argv);</p>
<p>		/*<br />
		 * Tell the parent we're ready to go<br />
		 */<br />
		close(child_ready_pipe[1]);</p>
<p>		/*<br />
		 * Wait until the parent tells us to go.<br />
		 */<br />
		if (read(go_pipe[0], &#038;buf, 1) == -1)<br />
			perror("unable to read pipe");</p>
<p>		execvp(argv[0], (char **)argv);</p>
<p>		perror(argv[0]);<br />
		exit(-1);<br />
	}</p>
<p>	child_pid = pid;</p>
<p>	/*<br />
	 * Wait for the child to be ready to exec.<br />
	 */<br />
	close(child_ready_pipe[1]);<br />
	close(go_pipe[0]);<br />
	if (read(child_ready_pipe[0], &#038;buf, 1) == -1)<br />
		perror("unable to read pipe");<br />
	close(child_ready_pipe[0]);</p>
<p>	for (counter = 0; counter < nr_counters; counter++)<br />
		create_perf_stat_counter(counter, pid);</p>
<p>	/*<br />
	 * Enable counters and exec the command:<br />
	 */<br />
	t0 = rdclock();</p>
<p>	close(go_pipe[1]);<br />
	wait(&#038;status);</p>
<p>	t1 = rdclock();</p>
<p>	update_stats(&#038;walltime_nsecs_stats, t1 - t0);</p>
<p>	for (counter = 0; counter < nr_counters; counter++)<br />
		read_counter(counter);</p>
<p>	return WEXITSTATUS(status);<br />
}</p>
<p>static void print_noise(int counter, double avg)<br />
{<br />
	if (run_count == 1)<br />
		return;</p>
<p>	fprintf(stderr, "   ( +- %7.3f%% )",<br />
			100 * stddev_stats(&#038;event_res_stats[counter][0]) / avg);<br />
}</p>
<p>static void nsec_printout(int counter, double avg)<br />
{<br />
	double msecs = avg / 1e6;</p>
<p>	fprintf(stderr, " %14.6f  %-24s", msecs, event_name(counter));</p>
<p>	if (MATCH_EVENT(SOFTWARE, SW_TASK_CLOCK, counter)) {<br />
		fprintf(stderr, " # %10.3f CPUs ",<br />
				avg / avg_stats(&#038;walltime_nsecs_stats));<br />
	}<br />
}</p>
<p>static void abs_printout(int counter, double avg)<br />
{<br />
	double total, ratio = 0.0;</p>
<p>	fprintf(stderr, " %14.0f  %-24s", avg, event_name(counter));</p>
<p>	if (MATCH_EVENT(HARDWARE, HW_INSTRUCTIONS, counter)) {<br />
		total = avg_stats(&#038;runtime_cycles_stats);</p>
<p>		if (total)<br />
			ratio = avg / total;</p>
<p>		fprintf(stderr, " # %10.3f IPC  ", ratio);<br />
	} else {<br />
		total = avg_stats(&#038;runtime_nsecs_stats);</p>
<p>		if (total)<br />
			ratio = 1000.0 * avg / total;</p>
<p>		fprintf(stderr, " # %10.3f M/sec", ratio);<br />
	}<br />
}</p>
<p>/*<br />
 * Print out the results of a single counter:<br />
 */<br />
static void print_counter(int counter)<br />
{<br />
	double avg = avg_stats(&#038;event_res_stats[counter][0]);<br />
	int scaled = event_scaled[counter];</p>
<p>	if (scaled == -1) {<br />
		fprintf(stderr, " %14s  %-24s\n",<br />
			"<not counted>&laquo;, event_name(counter));<br />
		return;<br />
	}</p>
<p>	if (nsec_counter(counter))<br />
		nsec_printout(counter, avg);<br />
	else<br />
		abs_printout(counter, avg);</p>
<p>	print_noise(counter, avg);</p>
<p>	if (scaled) {<br />
		double avg_enabled, avg_running;</p>
<p>		avg_enabled = avg_stats(&#038;event_res_stats[counter][1]);<br />
		avg_running = avg_stats(&#038;event_res_stats[counter][2]);</p>
<p>		fprintf(stderr, &raquo;  (scaled from %.2f%%)&raquo;,<br />
				100 * avg_running / avg_enabled);<br />
	}</p>
<p>	fprintf(stderr, &laquo;\n&raquo;);<br />
}</p>
<p>static void print_stat(int argc, const char **argv)<br />
{<br />
	int i, counter;</p>
<p>	fflush(stdout);</p>
<p>	fprintf(stderr, &laquo;\n&raquo;);<br />
	fprintf(stderr, &raquo; Performance counter stats for \&#8217;%s&raquo;, argv[0]);</p>
<p>	for (i = 1; i < argc; i++)<br />
		fprintf(stderr, " %s", argv[i]);</p>
<p>	fprintf(stderr, "\'");<br />
	if (run_count > 1)<br />
		fprintf(stderr, &raquo; (%d runs)&raquo;, run_count);<br />
	fprintf(stderr, &laquo;:\n\n&raquo;);</p>
<p>	for (counter = 0; counter < nr_counters; counter++)<br />
		print_counter(counter);</p>
<p>	fprintf(stderr, "\n");<br />
	fprintf(stderr, " %14.9f  seconds time elapsed",<br />
			avg_stats(&#038;walltime_nsecs_stats)/1e9);<br />
	if (run_count > 1) {<br />
		fprintf(stderr, &raquo;   ( +- %7.3f%% )&raquo;,<br />
				100*stddev_stats(&#038;walltime_nsecs_stats) /<br />
				avg_stats(&#038;walltime_nsecs_stats));<br />
	}<br />
	fprintf(stderr, &laquo;\n\n&raquo;);<br />
}</p>
<p>static volatile int signr = -1;</p>
<p>static void skip_signal(int signo)<br />
{<br />
	signr = signo;<br />
}</p>
<p>static void sig_atexit(void)<br />
{<br />
	if (child_pid != -1)<br />
		kill(child_pid, SIGTERM);</p>
<p>	if (signr == -1)<br />
		return;</p>
<p>	signal(signr, SIG_DFL);<br />
	kill(getpid(), signr);<br />
}</p>
<p>static const char * const stat_usage[] = {<br />
	&laquo;perf stat [<options>] <command>&laquo;,<br />
	NULL<br />
};</p>
<p>static const struct option options[] = {<br />
	OPT_CALLBACK(&#8217;e', &laquo;event&raquo;, NULL, &laquo;event&raquo;,<br />
		     &laquo;event selector. use &#8216;perf list&#8217; to list available events&raquo;,<br />
		     parse_events),<br />
	OPT_BOOLEAN(&#8217;i', &laquo;inherit&raquo;, &#038;inherit,<br />
		    &laquo;child tasks inherit counters&raquo;),<br />
	OPT_INTEGER(&#8217;p', &laquo;pid&raquo;, &#038;target_pid,<br />
		    &laquo;stat events on existing pid&raquo;),<br />
	OPT_BOOLEAN(&#8217;a', &laquo;all-cpus&raquo;, &#038;system_wide,<br />
		    &laquo;system-wide collection from all CPUs&raquo;),<br />
	OPT_BOOLEAN(&#8217;c', &laquo;scale&raquo;, &#038;scale,<br />
		    &laquo;scale/normalize counters&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show counter open errors, etc)&raquo;),<br />
	OPT_INTEGER(&#8217;r', &laquo;repeat&raquo;, &#038;run_count,<br />
		    &laquo;repeat command and print average + stddev (max: 100)&raquo;),<br />
	OPT_BOOLEAN(&#8217;n', &laquo;null&raquo;, &#038;null_run,<br />
		    &laquo;null run &#8211; dont start any counters&raquo;),<br />
	OPT_END()<br />
};</p>
<p>int cmd_stat(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	int status;</p>
<p>	argc = parse_options(argc, argv, options, stat_usage,<br />
		PARSE_OPT_STOP_AT_NON_OPTION);<br />
	if (!argc)<br />
		usage_with_options(stat_usage, options);<br />
	if (run_count <= 0)<br />
		usage_with_options(stat_usage, options);</p>
<p>	/* Set attrs and nr_counters if no event is selected and !null_run */<br />
	if (!null_run &#038;&#038; !nr_counters) {<br />
		memcpy(attrs, default_attrs, sizeof(default_attrs));<br />
		nr_counters = ARRAY_SIZE(default_attrs);<br />
	}</p>
<p>	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);<br />
	assert(nr_cpus <= MAX_NR_CPUS);<br />
	assert((int)nr_cpus >= 0);</p>
<p>	/*<br />
	 * We dont want to block the signals &#8211; that would cause<br />
	 * child tasks to inherit that and Ctrl-C would not work.<br />
	 * What we want is for Ctrl-C to work in the exec()-ed<br />
	 * task, but being ignored by perf stat itself:<br />
	 */<br />
	atexit(sig_atexit);<br />
	signal(SIGINT,  skip_signal);<br />
	signal(SIGALRM, skip_signal);<br />
	signal(SIGABRT, skip_signal);</p>
<p>	status = 0;<br />
	for (run_idx = 0; run_idx < run_count; run_idx++) {<br />
		if (run_count != 1 &#038;&#038; verbose)<br />
			fprintf(stderr, &laquo;[ perf stat: executing run #%d ... ]\n&raquo;, run_idx + 1);<br />
		status = run_perf_stat(argc, argv);<br />
	}</p>
<p>	print_stat(argc, argv);</p>
<p>	return status;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-stat-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-sched.c</title>
		<link>http://lynyrd.ru/builtin-sched-c</link>
		<comments>http://lynyrd.ru/builtin-sched-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:27:42 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=605</guid>
		<description><![CDATA[#include &#171;builtin.h&#187;
#include &#171;perf.h&#187;
#include &#171;util/util.h&#187;
#include &#171;util/cache.h&#187;
#include &#171;util/symbol.h&#187;
#include &#171;util/thread.h&#187;
#include &#171;util/header.h&#187;
#include &#171;util/parse-options.h&#187;
#include &#171;util/trace-event.h&#187;
#include &#171;util/debug.h&#187;
#include 
#include 
#include 
#include

#include

static char			const *input_name = &#171;perf.data&#187;;
static int			input;
static unsigned long		page_size;
static unsigned long		mmap_window = 32;
static unsigned long		total_comm = 0;
static struct rb_root		threads;
static struct thread		*last_match;
static struct perf_header	*header;
static u64			sample_type;
static char			default_sort_order[] = &#171;avg, max, switch, runtime&#187;;
static char			*sort_order = default_sort_order;
#define PR_SET_NAME		15          ]]></description>
			<content:encoded><![CDATA[<p>#include &laquo;builtin.h&raquo;<br />
#include &laquo;perf.h&raquo;<span id="more-605"></span></p>
<p>#include &laquo;util/util.h&raquo;<br />
#include &laquo;util/cache.h&raquo;<br />
#include &laquo;util/symbol.h&raquo;<br />
#include &laquo;util/thread.h&raquo;<br />
#include &laquo;util/header.h&raquo;</p>
<p>#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/trace-event.h&raquo;</p>
<p>#include &laquo;util/debug.h&raquo;</p>
<p>#include <sys/types.h><br />
#include <sys/prctl.h></p>
<p>#include <semaphore.h><br />
#include
<pthread.h>
#include<br />
<math.h>
<p>static char			const *input_name = &laquo;perf.data&raquo;;<br />
static int			input;<br />
static unsigned long		page_size;<br />
static unsigned long		mmap_window = 32;</p>
<p>static unsigned long		total_comm = 0;</p>
<p>static struct rb_root		threads;<br />
static struct thread		*last_match;</p>
<p>static struct perf_header	*header;<br />
static u64			sample_type;</p>
<p>static char			default_sort_order[] = &laquo;avg, max, switch, runtime&raquo;;<br />
static char			*sort_order = default_sort_order;</p>
<p>#define PR_SET_NAME		15               /* Set process name */<br />
#define MAX_CPUS		4096</p>
<p>#define BUG_ON(x)		assert(!(x))</p>
<p>static u64			run_measurement_overhead;<br />
static u64			sleep_measurement_overhead;</p>
<p>#define COMM_LEN		20<br />
#define SYM_LEN			129</p>
<p>#define MAX_PID			65536</p>
<p>static unsigned long		nr_tasks;</p>
<p>struct sched_atom;</p>
<p>struct task_desc {<br />
	unsigned long		nr;<br />
	unsigned long		pid;<br />
	char			comm[COMM_LEN];</p>
<p>	unsigned long		nr_events;<br />
	unsigned long		curr_event;<br />
	struct sched_atom	**atoms;</p>
<p>	pthread_t		thread;<br />
	sem_t			sleep_sem;</p>
<p>	sem_t			ready_for_work;<br />
	sem_t			work_done_sem;</p>
<p>	u64			cpu_usage;<br />
};</p>
<p>enum sched_event_type {<br />
	SCHED_EVENT_RUN,<br />
	SCHED_EVENT_SLEEP,<br />
	SCHED_EVENT_WAKEUP,<br />
};</p>
<p>struct sched_atom {<br />
	enum sched_event_type	type;<br />
	u64			timestamp;<br />
	u64			duration;<br />
	unsigned long		nr;<br />
	int			specific_wait;<br />
	sem_t			*wait_sem;<br />
	struct task_desc	*wakee;<br />
};</p>
<p>static struct task_desc		*pid_to_task[MAX_PID];</p>
<p>static struct task_desc		**tasks;</p>
<p>static pthread_mutex_t		start_work_mutex = PTHREAD_MUTEX_INITIALIZER;<br />
static u64			start_time;</p>
<p>static pthread_mutex_t		work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER;</p>
<p>static unsigned long		nr_run_events;<br />
static unsigned long		nr_sleep_events;<br />
static unsigned long		nr_wakeup_events;</p>
<p>static unsigned long		nr_sleep_corrections;<br />
static unsigned long		nr_run_events_optimized;</p>
<p>static unsigned long		targetless_wakeups;<br />
static unsigned long		multitarget_wakeups;</p>
<p>static u64			cpu_usage;<br />
static u64			runavg_cpu_usage;<br />
static u64			parent_cpu_usage;<br />
static u64			runavg_parent_cpu_usage;</p>
<p>static unsigned long		nr_runs;<br />
static u64			sum_runtime;<br />
static u64			sum_fluct;<br />
static u64			run_avg;</p>
<p>static unsigned long		replay_repeat = 10;<br />
static unsigned long		nr_timestamps;<br />
static unsigned long		nr_unordered_timestamps;<br />
static unsigned long		nr_state_machine_bugs;<br />
static unsigned long		nr_context_switch_bugs;<br />
static unsigned long		nr_events;<br />
static unsigned long		nr_lost_chunks;<br />
static unsigned long		nr_lost_events;</p>
<p>#define TASK_STATE_TO_CHAR_STR &laquo;RSDTtZX&raquo;</p>
<p>enum thread_state {<br />
	THREAD_SLEEPING = 0,<br />
	THREAD_WAIT_CPU,<br />
	THREAD_SCHED_IN,<br />
	THREAD_IGNORE<br />
};</p>
<p>struct work_atom {<br />
	struct list_head	list;<br />
	enum thread_state	state;<br />
	u64			sched_out_time;<br />
	u64			wake_up_time;<br />
	u64			sched_in_time;<br />
	u64			runtime;<br />
};</p>
<p>struct work_atoms {<br />
	struct list_head	work_list;<br />
	struct thread		*thread;<br />
	struct rb_node		node;<br />
	u64			max_lat;<br />
	u64			total_lat;<br />
	u64			nb_atoms;<br />
	u64			total_runtime;<br />
};</p>
<p>typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);</p>
<p>static struct rb_root		atom_root, sorted_atom_root;</p>
<p>static u64			all_runtime;<br />
static u64			all_count;</p>
<p>static u64 get_nsecs(void)<br />
{<br />
	struct timespec ts;</p>
<p>	clock_gettime(CLOCK_MONOTONIC, &#038;ts);</p>
<p>	return ts.tv_sec * 1000000000ULL + ts.tv_nsec;<br />
}</p>
<p>static void burn_nsecs(u64 nsecs)<br />
{<br />
	u64 T0 = get_nsecs(), T1;</p>
<p>	do {<br />
		T1 = get_nsecs();<br />
	} while (T1 + run_measurement_overhead < T0 + nsecs);<br />
}</p>
<p>static void sleep_nsecs(u64 nsecs)<br />
{<br />
	struct timespec ts;</p>
<p>	ts.tv_nsec = nsecs % 999999999;<br />
	ts.tv_sec = nsecs / 999999999;</p>
<p>	nanosleep(&#038;ts, NULL);<br />
}</p>
<p>static void calibrate_run_measurement_overhead(void)<br />
{<br />
	u64 T0, T1, delta, min_delta = 1000000000ULL;<br />
	int i;</p>
<p>	for (i = 0; i < 10; i++) {<br />
		T0 = get_nsecs();<br />
		burn_nsecs(0);<br />
		T1 = get_nsecs();<br />
		delta = T1-T0;<br />
		min_delta = min(min_delta, delta);<br />
	}<br />
	run_measurement_overhead = min_delta;</p>
<p>	printf("run measurement overhead: %Ld nsecs\n", min_delta);<br />
}</p>
<p>static void calibrate_sleep_measurement_overhead(void)<br />
{<br />
	u64 T0, T1, delta, min_delta = 1000000000ULL;<br />
	int i;</p>
<p>	for (i = 0; i < 10; i++) {<br />
		T0 = get_nsecs();<br />
		sleep_nsecs(10000);<br />
		T1 = get_nsecs();<br />
		delta = T1-T0;<br />
		min_delta = min(min_delta, delta);<br />
	}<br />
	min_delta -= 10000;<br />
	sleep_measurement_overhead = min_delta;</p>
<p>	printf("sleep measurement overhead: %Ld nsecs\n", min_delta);<br />
}</p>
<p>static struct sched_atom *<br />
get_new_event(struct task_desc *task, u64 timestamp)<br />
{<br />
	struct sched_atom *event = calloc(1, sizeof(*event));<br />
	unsigned long idx = task->nr_events;<br />
	size_t size;</p>
<p>	event->timestamp = timestamp;<br />
	event->nr = idx;</p>
<p>	task->nr_events++;<br />
	size = sizeof(struct sched_atom *) * task->nr_events;<br />
	task->atoms = realloc(task->atoms, size);<br />
	BUG_ON(!task->atoms);</p>
<p>	task->atoms[idx] = event;</p>
<p>	return event;<br />
}</p>
<p>static struct sched_atom *last_event(struct task_desc *task)<br />
{<br />
	if (!task->nr_events)<br />
		return NULL;</p>
<p>	return task->atoms[task->nr_events - 1];<br />
}</p>
<p>static void<br />
add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration)<br />
{<br />
	struct sched_atom *event, *curr_event = last_event(task);</p>
<p>	/*<br />
	 * optimize an existing RUN event by merging this one<br />
	 * to it:<br />
	 */<br />
	if (curr_event &#038;&#038; curr_event->type == SCHED_EVENT_RUN) {<br />
		nr_run_events_optimized++;<br />
		curr_event->duration += duration;<br />
		return;<br />
	}</p>
<p>	event = get_new_event(task, timestamp);</p>
<p>	event->type = SCHED_EVENT_RUN;<br />
	event->duration = duration;</p>
<p>	nr_run_events++;<br />
}</p>
<p>static void<br />
add_sched_event_wakeup(struct task_desc *task, u64 timestamp,<br />
		       struct task_desc *wakee)<br />
{<br />
	struct sched_atom *event, *wakee_event;</p>
<p>	event = get_new_event(task, timestamp);<br />
	event->type = SCHED_EVENT_WAKEUP;<br />
	event->wakee = wakee;</p>
<p>	wakee_event = last_event(wakee);<br />
	if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) {<br />
		targetless_wakeups++;<br />
		return;<br />
	}<br />
	if (wakee_event->wait_sem) {<br />
		multitarget_wakeups++;<br />
		return;<br />
	}</p>
<p>	wakee_event->wait_sem = calloc(1, sizeof(*wakee_event->wait_sem));<br />
	sem_init(wakee_event->wait_sem, 0, 0);<br />
	wakee_event->specific_wait = 1;<br />
	event->wait_sem = wakee_event->wait_sem;</p>
<p>	nr_wakeup_events++;<br />
}</p>
<p>static void<br />
add_sched_event_sleep(struct task_desc *task, u64 timestamp,<br />
		      u64 task_state __used)<br />
{<br />
	struct sched_atom *event = get_new_event(task, timestamp);</p>
<p>	event->type = SCHED_EVENT_SLEEP;</p>
<p>	nr_sleep_events++;<br />
}</p>
<p>static struct task_desc *register_pid(unsigned long pid, const char *comm)<br />
{<br />
	struct task_desc *task;</p>
<p>	BUG_ON(pid >= MAX_PID);</p>
<p>	task = pid_to_task[pid];</p>
<p>	if (task)<br />
		return task;</p>
<p>	task = calloc(1, sizeof(*task));<br />
	task->pid = pid;<br />
	task->nr = nr_tasks;<br />
	strcpy(task->comm, comm);<br />
	/*<br />
	 * every task starts in sleeping state &#8211; this gets ignored<br />
	 * if there&#8217;s no wakeup pointing to this sleep state:<br />
	 */<br />
	add_sched_event_sleep(task, 0, 0);</p>
<p>	pid_to_task[pid] = task;<br />
	nr_tasks++;<br />
	tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *));<br />
	BUG_ON(!tasks);<br />
	tasks[task->nr] = task;</p>
<p>	if (verbose)<br />
		printf(&raquo;registered task #%ld, PID %ld (%s)\n&raquo;, nr_tasks, pid, comm);</p>
<p>	return task;<br />
}</p>
<p>static void print_task_traces(void)<br />
{<br />
	struct task_desc *task;<br />
	unsigned long i;</p>
<p>	for (i = 0; i < nr_tasks; i++) {<br />
		task = tasks[i];<br />
		printf("task %6ld (%20s:%10ld), nr_events: %ld\n",<br />
			task->nr, task->comm, task->pid, task->nr_events);<br />
	}<br />
}</p>
<p>static void add_cross_task_wakeups(void)<br />
{<br />
	struct task_desc *task1, *task2;<br />
	unsigned long i, j;</p>
<p>	for (i = 0; i < nr_tasks; i++) {<br />
		task1 = tasks[i];<br />
		j = i + 1;<br />
		if (j == nr_tasks)<br />
			j = 0;<br />
		task2 = tasks[j];<br />
		add_sched_event_wakeup(task1, 0, task2);<br />
	}<br />
}</p>
<p>static void<br />
process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom)<br />
{<br />
	int ret = 0;<br />
	u64 now;<br />
	long long delta;</p>
<p>	now = get_nsecs();<br />
	delta = start_time + atom->timestamp &#8211; now;</p>
<p>	switch (atom->type) {<br />
		case SCHED_EVENT_RUN:<br />
			burn_nsecs(atom->duration);<br />
			break;<br />
		case SCHED_EVENT_SLEEP:<br />
			if (atom->wait_sem)<br />
				ret = sem_wait(atom->wait_sem);<br />
			BUG_ON(ret);<br />
			break;<br />
		case SCHED_EVENT_WAKEUP:<br />
			if (atom->wait_sem)<br />
				ret = sem_post(atom->wait_sem);<br />
			BUG_ON(ret);<br />
			break;<br />
		default:<br />
			BUG_ON(1);<br />
	}<br />
}</p>
<p>static u64 get_cpu_usage_nsec_parent(void)<br />
{<br />
	struct rusage ru;<br />
	u64 sum;<br />
	int err;</p>
<p>	err = getrusage(RUSAGE_SELF, &#038;ru);<br />
	BUG_ON(err);</p>
<p>	sum =  ru.ru_utime.tv_sec*1e9 + ru.ru_utime.tv_usec*1e3;<br />
	sum += ru.ru_stime.tv_sec*1e9 + ru.ru_stime.tv_usec*1e3;</p>
<p>	return sum;<br />
}</p>
<p>static u64 get_cpu_usage_nsec_self(void)<br />
{<br />
	char filename [] = &laquo;/proc/1234567890/sched&raquo;;<br />
	unsigned long msecs, nsecs;<br />
	char *line = NULL;<br />
	u64 total = 0;<br />
	size_t len = 0;<br />
	ssize_t chars;<br />
	FILE *file;<br />
	int ret;</p>
<p>	sprintf(filename, &laquo;/proc/%d/sched&raquo;, getpid());<br />
	file = fopen(filename, &laquo;r&raquo;);<br />
	BUG_ON(!file);</p>
<p>	while ((chars = getline(&#038;line, &#038;len, file)) != -1) {<br />
		ret = sscanf(line, &laquo;se.sum_exec_runtime : %ld.%06ld\n&raquo;,<br />
			&#038;msecs, &#038;nsecs);<br />
		if (ret == 2) {<br />
			total = msecs*1e6 + nsecs;<br />
			break;<br />
		}<br />
	}<br />
	if (line)<br />
		free(line);<br />
	fclose(file);</p>
<p>	return total;<br />
}</p>
<p>static void *thread_func(void *ctx)<br />
{<br />
	struct task_desc *this_task = ctx;<br />
	u64 cpu_usage_0, cpu_usage_1;<br />
	unsigned long i, ret;<br />
	char comm2[22];</p>
<p>	sprintf(comm2, &laquo;:%s&raquo;, this_task->comm);<br />
	prctl(PR_SET_NAME, comm2);</p>
<p>again:<br />
	ret = sem_post(&#038;this_task->ready_for_work);<br />
	BUG_ON(ret);<br />
	ret = pthread_mutex_lock(&#038;start_work_mutex);<br />
	BUG_ON(ret);<br />
	ret = pthread_mutex_unlock(&#038;start_work_mutex);<br />
	BUG_ON(ret);</p>
<p>	cpu_usage_0 = get_cpu_usage_nsec_self();</p>
<p>	for (i = 0; i < this_task->nr_events; i++) {<br />
		this_task->curr_event = i;<br />
		process_sched_event(this_task, this_task->atoms[i]);<br />
	}</p>
<p>	cpu_usage_1 = get_cpu_usage_nsec_self();<br />
	this_task->cpu_usage = cpu_usage_1 &#8211; cpu_usage_0;</p>
<p>	ret = sem_post(&#038;this_task->work_done_sem);<br />
	BUG_ON(ret);</p>
<p>	ret = pthread_mutex_lock(&#038;work_done_wait_mutex);<br />
	BUG_ON(ret);<br />
	ret = pthread_mutex_unlock(&#038;work_done_wait_mutex);<br />
	BUG_ON(ret);</p>
<p>	goto again;<br />
}</p>
<p>static void create_tasks(void)<br />
{<br />
	struct task_desc *task;<br />
	pthread_attr_t attr;<br />
	unsigned long i;<br />
	int err;</p>
<p>	err = pthread_attr_init(&#038;attr);<br />
	BUG_ON(err);<br />
	err = pthread_attr_setstacksize(&#038;attr, (size_t)(16*1024));<br />
	BUG_ON(err);<br />
	err = pthread_mutex_lock(&#038;start_work_mutex);<br />
	BUG_ON(err);<br />
	err = pthread_mutex_lock(&#038;work_done_wait_mutex);<br />
	BUG_ON(err);<br />
	for (i = 0; i < nr_tasks; i++) {<br />
		task = tasks[i];<br />
		sem_init(&#038;task->sleep_sem, 0, 0);<br />
		sem_init(&#038;task->ready_for_work, 0, 0);<br />
		sem_init(&#038;task->work_done_sem, 0, 0);<br />
		task->curr_event = 0;<br />
		err = pthread_create(&#038;task->thread, &#038;attr, thread_func, task);<br />
		BUG_ON(err);<br />
	}<br />
}</p>
<p>static void wait_for_tasks(void)<br />
{<br />
	u64 cpu_usage_0, cpu_usage_1;<br />
	struct task_desc *task;<br />
	unsigned long i, ret;</p>
<p>	start_time = get_nsecs();<br />
	cpu_usage = 0;<br />
	pthread_mutex_unlock(&#038;work_done_wait_mutex);</p>
<p>	for (i = 0; i < nr_tasks; i++) {<br />
		task = tasks[i];<br />
		ret = sem_wait(&#038;task->ready_for_work);<br />
		BUG_ON(ret);<br />
		sem_init(&#038;task->ready_for_work, 0, 0);<br />
	}<br />
	ret = pthread_mutex_lock(&#038;work_done_wait_mutex);<br />
	BUG_ON(ret);</p>
<p>	cpu_usage_0 = get_cpu_usage_nsec_parent();</p>
<p>	pthread_mutex_unlock(&#038;start_work_mutex);</p>
<p>	for (i = 0; i < nr_tasks; i++) {<br />
		task = tasks[i];<br />
		ret = sem_wait(&#038;task->work_done_sem);<br />
		BUG_ON(ret);<br />
		sem_init(&#038;task->work_done_sem, 0, 0);<br />
		cpu_usage += task->cpu_usage;<br />
		task->cpu_usage = 0;<br />
	}</p>
<p>	cpu_usage_1 = get_cpu_usage_nsec_parent();<br />
	if (!runavg_cpu_usage)<br />
		runavg_cpu_usage = cpu_usage;<br />
	runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10;</p>
<p>	parent_cpu_usage = cpu_usage_1 &#8211; cpu_usage_0;<br />
	if (!runavg_parent_cpu_usage)<br />
		runavg_parent_cpu_usage = parent_cpu_usage;<br />
	runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 +<br />
				   parent_cpu_usage)/10;</p>
<p>	ret = pthread_mutex_lock(&#038;start_work_mutex);<br />
	BUG_ON(ret);</p>
<p>	for (i = 0; i < nr_tasks; i++) {<br />
		task = tasks[i];<br />
		sem_init(&#038;task->sleep_sem, 0, 0);<br />
		task->curr_event = 0;<br />
	}<br />
}</p>
<p>static void run_one_test(void)<br />
{<br />
	u64 T0, T1, delta, avg_delta, fluct, std_dev;</p>
<p>	T0 = get_nsecs();<br />
	wait_for_tasks();<br />
	T1 = get_nsecs();</p>
<p>	delta = T1 &#8211; T0;<br />
	sum_runtime += delta;<br />
	nr_runs++;</p>
<p>	avg_delta = sum_runtime / nr_runs;<br />
	if (delta < avg_delta)<br />
		fluct = avg_delta - delta;<br />
	else<br />
		fluct = delta - avg_delta;<br />
	sum_fluct += fluct;<br />
	std_dev = sum_fluct / nr_runs / sqrt(nr_runs);<br />
	if (!run_avg)<br />
		run_avg = delta;<br />
	run_avg = (run_avg*9 + delta)/10;</p>
<p>	printf("#%-3ld: %0.3f, ",<br />
		nr_runs, (double)delta/1000000.0);</p>
<p>	printf("ravg: %0.2f, ",<br />
		(double)run_avg/1e6);</p>
<p>	printf("cpu: %0.2f / %0.2f",<br />
		(double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6);</p>
<p>#if 0<br />
	/*<br />
	 * rusage statistics done by the parent, these are less<br />
	 * accurate than the sum_exec_runtime based statistics:<br />
	 */<br />
	printf(" [%0.2f / %0.2f]",<br />
		(double)parent_cpu_usage/1e6,<br />
		(double)runavg_parent_cpu_usage/1e6);<br />
#endif</p>
<p>	printf("\n");</p>
<p>	if (nr_sleep_corrections)<br />
		printf(" (%ld sleep corrections)\n", nr_sleep_corrections);<br />
	nr_sleep_corrections = 0;<br />
}</p>
<p>static void test_calibrations(void)<br />
{<br />
	u64 T0, T1;</p>
<p>	T0 = get_nsecs();<br />
	burn_nsecs(1e6);<br />
	T1 = get_nsecs();</p>
<p>	printf("the run test took %Ld nsecs\n", T1-T0);</p>
<p>	T0 = get_nsecs();<br />
	sleep_nsecs(1e6);<br />
	T1 = get_nsecs();</p>
<p>	printf("the sleep test took %Ld nsecs\n", T1-T0);<br />
}</p>
<p>static int<br />
process_comm_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;</p>
<p>	thread = threads__findnew(event->comm.pid, &#038;threads, &#038;last_match);</p>
<p>	dump_printf(&raquo;%p [%p]: perf_event_comm: %s:%d\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->comm.comm, event->comm.pid);</p>
<p>	if (thread == NULL ||<br />
	    thread__set_comm(thread, event->comm.comm)) {<br />
		dump_printf(&raquo;problem processing perf_event_comm, skipping event.\n&raquo;);<br />
		return -1;<br />
	}<br />
	total_comm++;</p>
<p>	return 0;<br />
}</p>
<p>struct raw_event_sample {<br />
	u32 size;<br />
	char data[0];<br />
};</p>
<p>#define FILL_FIELD(ptr, field, event, data)	\<br />
	ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data)</p>
<p>#define FILL_ARRAY(ptr, array, event, data)			\<br />
do {								\<br />
	void *__array = raw_field_ptr(event, #array, data);	\<br />
	memcpy(ptr.array, __array, sizeof(ptr.array));	\<br />
} while(0)</p>
<p>#define FILL_COMMON_FIELDS(ptr, event, data)			\<br />
do {								\<br />
	FILL_FIELD(ptr, common_type, event, data);		\<br />
	FILL_FIELD(ptr, common_flags, event, data);		\<br />
	FILL_FIELD(ptr, common_preempt_count, event, data);	\<br />
	FILL_FIELD(ptr, common_pid, event, data);		\<br />
	FILL_FIELD(ptr, common_tgid, event, data);		\<br />
} while (0)</p>
<p>struct trace_switch_event {<br />
	u32 size;</p>
<p>	u16 common_type;<br />
	u8 common_flags;<br />
	u8 common_preempt_count;<br />
	u32 common_pid;<br />
	u32 common_tgid;</p>
<p>	char prev_comm[16];<br />
	u32 prev_pid;<br />
	u32 prev_prio;<br />
	u64 prev_state;<br />
	char next_comm[16];<br />
	u32 next_pid;<br />
	u32 next_prio;<br />
};</p>
<p>struct trace_runtime_event {<br />
	u32 size;</p>
<p>	u16 common_type;<br />
	u8 common_flags;<br />
	u8 common_preempt_count;<br />
	u32 common_pid;<br />
	u32 common_tgid;</p>
<p>	char comm[16];<br />
	u32 pid;<br />
	u64 runtime;<br />
	u64 vruntime;<br />
};</p>
<p>struct trace_wakeup_event {<br />
	u32 size;</p>
<p>	u16 common_type;<br />
	u8 common_flags;<br />
	u8 common_preempt_count;<br />
	u32 common_pid;<br />
	u32 common_tgid;</p>
<p>	char comm[16];<br />
	u32 pid;</p>
<p>	u32 prio;<br />
	u32 success;<br />
	u32 cpu;<br />
};</p>
<p>struct trace_fork_event {<br />
	u32 size;</p>
<p>	u16 common_type;<br />
	u8 common_flags;<br />
	u8 common_preempt_count;<br />
	u32 common_pid;<br />
	u32 common_tgid;</p>
<p>	char parent_comm[16];<br />
	u32 parent_pid;<br />
	char child_comm[16];<br />
	u32 child_pid;<br />
};</p>
<p>struct trace_sched_handler {<br />
	void (*switch_event)(struct trace_switch_event *,<br />
			     struct event *,<br />
			     int cpu,<br />
			     u64 timestamp,<br />
			     struct thread *thread);</p>
<p>	void (*runtime_event)(struct trace_runtime_event *,<br />
			      struct event *,<br />
			      int cpu,<br />
			      u64 timestamp,<br />
			      struct thread *thread);</p>
<p>	void (*wakeup_event)(struct trace_wakeup_event *,<br />
			     struct event *,<br />
			     int cpu,<br />
			     u64 timestamp,<br />
			     struct thread *thread);</p>
<p>	void (*fork_event)(struct trace_fork_event *,<br />
			   struct event *,<br />
			   int cpu,<br />
			   u64 timestamp,<br />
			   struct thread *thread);<br />
};</p>
<p>static void<br />
replay_wakeup_event(struct trace_wakeup_event *wakeup_event,<br />
		    struct event *event,<br />
		    int cpu __used,<br />
		    u64 timestamp __used,<br />
		    struct thread *thread __used)<br />
{<br />
	struct task_desc *waker, *wakee;</p>
<p>	if (verbose) {<br />
		printf(&raquo;sched_wakeup event %p\n&raquo;, event);</p>
<p>		printf(&raquo; &#8230; pid %d woke up %s/%d\n&raquo;,<br />
			wakeup_event->common_pid,<br />
			wakeup_event->comm,<br />
			wakeup_event->pid);<br />
	}</p>
<p>	waker = register_pid(wakeup_event->common_pid, &laquo;<unknown>&laquo;);<br />
	wakee = register_pid(wakeup_event->pid, wakeup_event->comm);</p>
<p>	add_sched_event_wakeup(waker, timestamp, wakee);<br />
}</p>
<p>static u64 cpu_last_switched[MAX_CPUS];</p>
<p>static void<br />
replay_switch_event(struct trace_switch_event *switch_event,<br />
		    struct event *event,<br />
		    int cpu,<br />
		    u64 timestamp,<br />
		    struct thread *thread __used)<br />
{<br />
	struct task_desc *prev, *next;<br />
	u64 timestamp0;<br />
	s64 delta;</p>
<p>	if (verbose)<br />
		printf(&raquo;sched_switch event %p\n&raquo;, event);</p>
<p>	if (cpu >= MAX_CPUS || cpu < 0)<br />
		return;</p>
<p>	timestamp0 = cpu_last_switched[cpu];<br />
	if (timestamp0)<br />
		delta = timestamp - timestamp0;<br />
	else<br />
		delta = 0;</p>
<p>	if (delta < 0)<br />
		die("hm, delta: %Ld < 0 ?\n", delta);</p>
<p>	if (verbose) {<br />
		printf(" ... switch from %s/%d to %s/%d [ran %Ld nsecs]\n",<br />
			switch_event->prev_comm, switch_event->prev_pid,<br />
			switch_event->next_comm, switch_event->next_pid,<br />
			delta);<br />
	}</p>
<p>	prev = register_pid(switch_event->prev_pid, switch_event->prev_comm);<br />
	next = register_pid(switch_event->next_pid, switch_event->next_comm);</p>
<p>	cpu_last_switched[cpu] = timestamp;</p>
<p>	add_sched_event_run(prev, timestamp, delta);<br />
	add_sched_event_sleep(prev, timestamp, switch_event->prev_state);<br />
}</p>
<p>static void<br />
replay_fork_event(struct trace_fork_event *fork_event,<br />
		  struct event *event,<br />
		  int cpu __used,<br />
		  u64 timestamp __used,<br />
		  struct thread *thread __used)<br />
{<br />
	if (verbose) {<br />
		printf(&raquo;sched_fork event %p\n&raquo;, event);<br />
		printf(&raquo;&#8230; parent: %s/%d\n&raquo;, fork_event->parent_comm, fork_event->parent_pid);<br />
		printf(&raquo;&#8230;  child: %s/%d\n&raquo;, fork_event->child_comm, fork_event->child_pid);<br />
	}<br />
	register_pid(fork_event->parent_pid, fork_event->parent_comm);<br />
	register_pid(fork_event->child_pid, fork_event->child_comm);<br />
}</p>
<p>static struct trace_sched_handler replay_ops  = {<br />
	.wakeup_event		= replay_wakeup_event,<br />
	.switch_event		= replay_switch_event,<br />
	.fork_event		= replay_fork_event,<br />
};</p>
<p>struct sort_dimension {<br />
	const char		*name;<br />
	sort_fn_t		cmp;<br />
	struct list_head	list;<br />
};</p>
<p>static LIST_HEAD(cmp_pid);</p>
<p>static int<br />
thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)<br />
{<br />
	struct sort_dimension *sort;<br />
	int ret = 0;</p>
<p>	BUG_ON(list_empty(list));</p>
<p>	list_for_each_entry(sort, list, list) {<br />
		ret = sort->cmp(l, r);<br />
		if (ret)<br />
			return ret;<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static struct work_atoms *<br />
thread_atoms_search(struct rb_root *root, struct thread *thread,<br />
			 struct list_head *sort_list)<br />
{<br />
	struct rb_node *node = root->rb_node;<br />
	struct work_atoms key = { .thread = thread };</p>
<p>	while (node) {<br />
		struct work_atoms *atoms;<br />
		int cmp;</p>
<p>		atoms = container_of(node, struct work_atoms, node);</p>
<p>		cmp = thread_lat_cmp(sort_list, &#038;key, atoms);<br />
		if (cmp > 0)<br />
			node = node->rb_left;<br />
		else if (cmp < 0)<br />
			node = node->rb_right;<br />
		else {<br />
			BUG_ON(thread != atoms->thread);<br />
			return atoms;<br />
		}<br />
	}<br />
	return NULL;<br />
}</p>
<p>static void<br />
__thread_latency_insert(struct rb_root *root, struct work_atoms *data,<br />
			 struct list_head *sort_list)<br />
{<br />
	struct rb_node **new = &#038;(root->rb_node), *parent = NULL;</p>
<p>	while (*new) {<br />
		struct work_atoms *this;<br />
		int cmp;</p>
<p>		this = container_of(*new, struct work_atoms, node);<br />
		parent = *new;</p>
<p>		cmp = thread_lat_cmp(sort_list, data, this);</p>
<p>		if (cmp > 0)<br />
			new = &#038;((*new)->rb_left);<br />
		else<br />
			new = &#038;((*new)->rb_right);<br />
	}</p>
<p>	rb_link_node(&#038;data->node, parent, new);<br />
	rb_insert_color(&#038;data->node, root);<br />
}</p>
<p>static void thread_atoms_insert(struct thread *thread)<br />
{<br />
	struct work_atoms *atoms;</p>
<p>	atoms = calloc(sizeof(*atoms), 1);<br />
	if (!atoms)<br />
		die(&raquo;No memory&raquo;);</p>
<p>	atoms->thread = thread;<br />
	INIT_LIST_HEAD(&#038;atoms->work_list);<br />
	__thread_latency_insert(&#038;atom_root, atoms, &#038;cmp_pid);<br />
}</p>
<p>static void<br />
latency_fork_event(struct trace_fork_event *fork_event __used,<br />
		   struct event *event __used,<br />
		   int cpu __used,<br />
		   u64 timestamp __used,<br />
		   struct thread *thread __used)<br />
{<br />
	/* should insert the newcomer */<br />
}</p>
<p>__used<br />
static char sched_out_state(struct trace_switch_event *switch_event)<br />
{<br />
	const char *str = TASK_STATE_TO_CHAR_STR;</p>
<p>	return str[switch_event->prev_state];<br />
}</p>
<p>static void<br />
add_sched_out_event(struct work_atoms *atoms,<br />
		    char run_state,<br />
		    u64 timestamp)<br />
{<br />
	struct work_atom *atom;</p>
<p>	atom = calloc(sizeof(*atom), 1);<br />
	if (!atom)<br />
		die(&raquo;Non memory&raquo;);</p>
<p>	atom->sched_out_time = timestamp;</p>
<p>	if (run_state == &#8216;R&#8217;) {<br />
		atom->state = THREAD_WAIT_CPU;<br />
		atom->wake_up_time = atom->sched_out_time;<br />
	}</p>
<p>	list_add_tail(&#038;atom->list, &#038;atoms->work_list);<br />
}</p>
<p>static void<br />
add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used)<br />
{<br />
	struct work_atom *atom;</p>
<p>	BUG_ON(list_empty(&#038;atoms->work_list));</p>
<p>	atom = list_entry(atoms->work_list.prev, struct work_atom, list);</p>
<p>	atom->runtime += delta;<br />
	atoms->total_runtime += delta;<br />
}</p>
<p>static void<br />
add_sched_in_event(struct work_atoms *atoms, u64 timestamp)<br />
{<br />
	struct work_atom *atom;<br />
	u64 delta;</p>
<p>	if (list_empty(&#038;atoms->work_list))<br />
		return;</p>
<p>	atom = list_entry(atoms->work_list.prev, struct work_atom, list);</p>
<p>	if (atom->state != THREAD_WAIT_CPU)<br />
		return;</p>
<p>	if (timestamp < atom->wake_up_time) {<br />
		atom->state = THREAD_IGNORE;<br />
		return;<br />
	}</p>
<p>	atom->state = THREAD_SCHED_IN;<br />
	atom->sched_in_time = timestamp;</p>
<p>	delta = atom->sched_in_time &#8211; atom->wake_up_time;<br />
	atoms->total_lat += delta;<br />
	if (delta > atoms->max_lat)<br />
		atoms->max_lat = delta;<br />
	atoms->nb_atoms++;<br />
}</p>
<p>static void<br />
latency_switch_event(struct trace_switch_event *switch_event,<br />
		     struct event *event __used,<br />
		     int cpu,<br />
		     u64 timestamp,<br />
		     struct thread *thread __used)<br />
{<br />
	struct work_atoms *out_events, *in_events;<br />
	struct thread *sched_out, *sched_in;<br />
	u64 timestamp0;<br />
	s64 delta;</p>
<p>	BUG_ON(cpu >= MAX_CPUS || cpu < 0);</p>
<p>	timestamp0 = cpu_last_switched[cpu];<br />
	cpu_last_switched[cpu] = timestamp;<br />
	if (timestamp0)<br />
		delta = timestamp - timestamp0;<br />
	else<br />
		delta = 0;</p>
<p>	if (delta < 0)<br />
		die("hm, delta: %Ld < 0 ?\n", delta);</p>
<p>	sched_out = threads__findnew(switch_event->prev_pid, &#038;threads, &#038;last_match);<br />
	sched_in = threads__findnew(switch_event->next_pid, &#038;threads, &#038;last_match);</p>
<p>	out_events = thread_atoms_search(&#038;atom_root, sched_out, &#038;cmp_pid);<br />
	if (!out_events) {<br />
		thread_atoms_insert(sched_out);<br />
		out_events = thread_atoms_search(&#038;atom_root, sched_out, &#038;cmp_pid);<br />
		if (!out_events)<br />
			die(&raquo;out-event: Internal tree error&raquo;);<br />
	}<br />
	add_sched_out_event(out_events, sched_out_state(switch_event), timestamp);</p>
<p>	in_events = thread_atoms_search(&#038;atom_root, sched_in, &#038;cmp_pid);<br />
	if (!in_events) {<br />
		thread_atoms_insert(sched_in);<br />
		in_events = thread_atoms_search(&#038;atom_root, sched_in, &#038;cmp_pid);<br />
		if (!in_events)<br />
			die(&raquo;in-event: Internal tree error&raquo;);<br />
		/*<br />
		 * Take came in we have not heard about yet,<br />
		 * add in an initial atom in runnable state:<br />
		 */<br />
		add_sched_out_event(in_events, &#8216;R&#8217;, timestamp);<br />
	}<br />
	add_sched_in_event(in_events, timestamp);<br />
}</p>
<p>static void<br />
latency_runtime_event(struct trace_runtime_event *runtime_event,<br />
		     struct event *event __used,<br />
		     int cpu,<br />
		     u64 timestamp,<br />
		     struct thread *this_thread __used)<br />
{<br />
	struct work_atoms *atoms;<br />
	struct thread *thread;</p>
<p>	BUG_ON(cpu >= MAX_CPUS || cpu < 0);</p>
<p>	thread = threads__findnew(runtime_event->pid, &#038;threads, &#038;last_match);<br />
	atoms = thread_atoms_search(&#038;atom_root, thread, &#038;cmp_pid);<br />
	if (!atoms) {<br />
		thread_atoms_insert(thread);<br />
		atoms = thread_atoms_search(&#038;atom_root, thread, &#038;cmp_pid);<br />
		if (!atoms)<br />
			die(&raquo;in-event: Internal tree error&raquo;);<br />
		add_sched_out_event(atoms, &#8216;R&#8217;, timestamp);<br />
	}</p>
<p>	add_runtime_event(atoms, runtime_event->runtime, timestamp);<br />
}</p>
<p>static void<br />
latency_wakeup_event(struct trace_wakeup_event *wakeup_event,<br />
		     struct event *__event __used,<br />
		     int cpu __used,<br />
		     u64 timestamp,<br />
		     struct thread *thread __used)<br />
{<br />
	struct work_atoms *atoms;<br />
	struct work_atom *atom;<br />
	struct thread *wakee;</p>
<p>	/* Note for later, it may be interesting to observe the failing cases */<br />
	if (!wakeup_event->success)<br />
		return;</p>
<p>	wakee = threads__findnew(wakeup_event->pid, &#038;threads, &#038;last_match);<br />
	atoms = thread_atoms_search(&#038;atom_root, wakee, &#038;cmp_pid);<br />
	if (!atoms) {<br />
		thread_atoms_insert(wakee);<br />
		atoms = thread_atoms_search(&#038;atom_root, wakee, &#038;cmp_pid);<br />
		if (!atoms)<br />
			die(&raquo;wakeup-event: Internal tree error&raquo;);<br />
		add_sched_out_event(atoms, &#8216;S&#8217;, timestamp);<br />
	}</p>
<p>	BUG_ON(list_empty(&#038;atoms->work_list));</p>
<p>	atom = list_entry(atoms->work_list.prev, struct work_atom, list);</p>
<p>	if (atom->state != THREAD_SLEEPING)<br />
		nr_state_machine_bugs++;</p>
<p>	nr_timestamps++;<br />
	if (atom->sched_out_time > timestamp) {<br />
		nr_unordered_timestamps++;<br />
		return;<br />
	}</p>
<p>	atom->state = THREAD_WAIT_CPU;<br />
	atom->wake_up_time = timestamp;<br />
}</p>
<p>static struct trace_sched_handler lat_ops  = {<br />
	.wakeup_event		= latency_wakeup_event,<br />
	.switch_event		= latency_switch_event,<br />
	.runtime_event		= latency_runtime_event,<br />
	.fork_event		= latency_fork_event,<br />
};</p>
<p>static void output_lat_thread(struct work_atoms *work_list)<br />
{<br />
	int i;<br />
	int ret;<br />
	u64 avg;</p>
<p>	if (!work_list->nb_atoms)<br />
		return;<br />
	/*<br />
	 * Ignore idle threads:<br />
	 */<br />
	if (!strcmp(work_list->thread->comm, &laquo;swapper&raquo;))<br />
		return;</p>
<p>	all_runtime += work_list->total_runtime;<br />
	all_count += work_list->nb_atoms;</p>
<p>	ret = printf(&raquo;  %s:%d &laquo;, work_list->thread->comm, work_list->thread->pid);</p>
<p>	for (i = 0; i < 24 - ret; i++)<br />
		printf(" ");</p>
<p>	avg = work_list->total_lat / work_list->nb_atoms;</p>
<p>	printf(&raquo;|%11.3f ms |%9llu | avg:%9.3f ms | max:%9.3f ms |\n&raquo;,<br />
	      (double)work_list->total_runtime / 1e6,<br />
		 work_list->nb_atoms, (double)avg / 1e6,<br />
		 (double)work_list->max_lat / 1e6);<br />
}</p>
<p>static int pid_cmp(struct work_atoms *l, struct work_atoms *r)<br />
{<br />
	if (l->thread->pid < r->thread->pid)<br />
		return -1;<br />
	if (l->thread->pid > r->thread->pid)<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>static struct sort_dimension pid_sort_dimension = {<br />
	.name			= &laquo;pid&raquo;,<br />
	.cmp			= pid_cmp,<br />
};</p>
<p>static int avg_cmp(struct work_atoms *l, struct work_atoms *r)<br />
{<br />
	u64 avgl, avgr;</p>
<p>	if (!l->nb_atoms)<br />
		return -1;</p>
<p>	if (!r->nb_atoms)<br />
		return 1;</p>
<p>	avgl = l->total_lat / l->nb_atoms;<br />
	avgr = r->total_lat / r->nb_atoms;</p>
<p>	if (avgl < avgr)<br />
		return -1;<br />
	if (avgl > avgr)<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>static struct sort_dimension avg_sort_dimension = {<br />
	.name			= &laquo;avg&raquo;,<br />
	.cmp			= avg_cmp,<br />
};</p>
<p>static int max_cmp(struct work_atoms *l, struct work_atoms *r)<br />
{<br />
	if (l->max_lat < r->max_lat)<br />
		return -1;<br />
	if (l->max_lat > r->max_lat)<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>static struct sort_dimension max_sort_dimension = {<br />
	.name			= &laquo;max&raquo;,<br />
	.cmp			= max_cmp,<br />
};</p>
<p>static int switch_cmp(struct work_atoms *l, struct work_atoms *r)<br />
{<br />
	if (l->nb_atoms < r->nb_atoms)<br />
		return -1;<br />
	if (l->nb_atoms > r->nb_atoms)<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>static struct sort_dimension switch_sort_dimension = {<br />
	.name			= &laquo;switch&raquo;,<br />
	.cmp			= switch_cmp,<br />
};</p>
<p>static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)<br />
{<br />
	if (l->total_runtime < r->total_runtime)<br />
		return -1;<br />
	if (l->total_runtime > r->total_runtime)<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>static struct sort_dimension runtime_sort_dimension = {<br />
	.name			= &laquo;runtime&raquo;,<br />
	.cmp			= runtime_cmp,<br />
};</p>
<p>static struct sort_dimension *available_sorts[] = {<br />
	&#038;pid_sort_dimension,<br />
	&#038;avg_sort_dimension,<br />
	&#038;max_sort_dimension,<br />
	&#038;switch_sort_dimension,<br />
	&#038;runtime_sort_dimension,<br />
};</p>
<p>#define NB_AVAILABLE_SORTS	(int)(sizeof(available_sorts) / sizeof(struct sort_dimension *))</p>
<p>static LIST_HEAD(sort_list);</p>
<p>static int sort_dimension__add(const char *tok, struct list_head *list)<br />
{<br />
	int i;</p>
<p>	for (i = 0; i < NB_AVAILABLE_SORTS; i++) {<br />
		if (!strcmp(available_sorts[i]->name, tok)) {<br />
			list_add_tail(&#038;available_sorts[i]->list, list);</p>
<p>			return 0;<br />
		}<br />
	}</p>
<p>	return -1;<br />
}</p>
<p>static void setup_sorting(void);</p>
<p>static void sort_lat(void)<br />
{<br />
	struct rb_node *node;</p>
<p>	for (;;) {<br />
		struct work_atoms *data;<br />
		node = rb_first(&#038;atom_root);<br />
		if (!node)<br />
			break;</p>
<p>		rb_erase(node, &#038;atom_root);<br />
		data = rb_entry(node, struct work_atoms, node);<br />
		__thread_latency_insert(&#038;sorted_atom_root, data, &#038;sort_list);<br />
	}<br />
}</p>
<p>static struct trace_sched_handler *trace_handler;</p>
<p>static void<br />
process_sched_wakeup_event(struct raw_event_sample *raw,<br />
			   struct event *event,<br />
			   int cpu __used,<br />
			   u64 timestamp __used,<br />
			   struct thread *thread __used)<br />
{<br />
	struct trace_wakeup_event wakeup_event;</p>
<p>	FILL_COMMON_FIELDS(wakeup_event, event, raw->data);</p>
<p>	FILL_ARRAY(wakeup_event, comm, event, raw->data);<br />
	FILL_FIELD(wakeup_event, pid, event, raw->data);<br />
	FILL_FIELD(wakeup_event, prio, event, raw->data);<br />
	FILL_FIELD(wakeup_event, success, event, raw->data);<br />
	FILL_FIELD(wakeup_event, cpu, event, raw->data);</p>
<p>	if (trace_handler->wakeup_event)<br />
		trace_handler->wakeup_event(&#038;wakeup_event, event, cpu, timestamp, thread);<br />
}</p>
<p>/*<br />
 * Track the current task &#8211; that way we can know whether there&#8217;s any<br />
 * weird events, such as a task being switched away that is not current.<br />
 */<br />
static int max_cpu;</p>
<p>static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 };</p>
<p>static struct thread *curr_thread[MAX_CPUS];</p>
<p>static char next_shortname1 = &#8216;A&#8217;;<br />
static char next_shortname2 = &#8216;0&#8242;;</p>
<p>static void<br />
map_switch_event(struct trace_switch_event *switch_event,<br />
		 struct event *event __used,<br />
		 int this_cpu,<br />
		 u64 timestamp,<br />
		 struct thread *thread __used)<br />
{<br />
	struct thread *sched_out, *sched_in;<br />
	int new_shortname;<br />
	u64 timestamp0;<br />
	s64 delta;<br />
	int cpu;</p>
<p>	BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);</p>
<p>	if (this_cpu > max_cpu)<br />
		max_cpu = this_cpu;</p>
<p>	timestamp0 = cpu_last_switched[this_cpu];<br />
	cpu_last_switched[this_cpu] = timestamp;<br />
	if (timestamp0)<br />
		delta = timestamp &#8211; timestamp0;<br />
	else<br />
		delta = 0;</p>
<p>	if (delta < 0)<br />
		die("hm, delta: %Ld < 0 ?\n", delta);</p>
<p>	sched_out = threads__findnew(switch_event->prev_pid, &#038;threads, &#038;last_match);<br />
	sched_in = threads__findnew(switch_event->next_pid, &#038;threads, &#038;last_match);</p>
<p>	curr_thread[this_cpu] = sched_in;</p>
<p>	printf(&raquo;  &laquo;);</p>
<p>	new_shortname = 0;<br />
	if (!sched_in->shortname[0]) {<br />
		sched_in->shortname[0] = next_shortname1;<br />
		sched_in->shortname[1] = next_shortname2;</p>
<p>		if (next_shortname1 < 'Z') {<br />
			next_shortname1++;<br />
		} else {<br />
			next_shortname1='A';<br />
			if (next_shortname2 < '9') {<br />
				next_shortname2++;<br />
			} else {<br />
				next_shortname2='0';<br />
			}<br />
		}<br />
		new_shortname = 1;<br />
	}</p>
<p>	for (cpu = 0; cpu <= max_cpu; cpu++) {<br />
		if (cpu != this_cpu)<br />
			printf(" ");<br />
		else<br />
			printf("*");</p>
<p>		if (curr_thread[cpu]) {<br />
			if (curr_thread[cpu]->pid)<br />
				printf(&raquo;%2s &laquo;, curr_thread[cpu]->shortname);<br />
			else<br />
				printf(&raquo;.  &laquo;);<br />
		} else<br />
			printf(&raquo;   &laquo;);<br />
	}</p>
<p>	printf(&raquo;  %12.6f secs &laquo;, (double)timestamp/1e9);<br />
	if (new_shortname) {<br />
		printf(&raquo;%s => %s:%d\n&raquo;,<br />
			sched_in->shortname, sched_in->comm, sched_in->pid);<br />
	} else {<br />
		printf(&raquo;\n&raquo;);<br />
	}<br />
}</p>
<p>static void<br />
process_sched_switch_event(struct raw_event_sample *raw,<br />
			   struct event *event,<br />
			   int this_cpu,<br />
			   u64 timestamp __used,<br />
			   struct thread *thread __used)<br />
{<br />
	struct trace_switch_event switch_event;</p>
<p>	FILL_COMMON_FIELDS(switch_event, event, raw->data);</p>
<p>	FILL_ARRAY(switch_event, prev_comm, event, raw->data);<br />
	FILL_FIELD(switch_event, prev_pid, event, raw->data);<br />
	FILL_FIELD(switch_event, prev_prio, event, raw->data);<br />
	FILL_FIELD(switch_event, prev_state, event, raw->data);<br />
	FILL_ARRAY(switch_event, next_comm, event, raw->data);<br />
	FILL_FIELD(switch_event, next_pid, event, raw->data);<br />
	FILL_FIELD(switch_event, next_prio, event, raw->data);</p>
<p>	if (curr_pid[this_cpu] != (u32)-1) {<br />
		/*<br />
		 * Are we trying to switch away a PID that is<br />
		 * not current?<br />
		 */<br />
		if (curr_pid[this_cpu] != switch_event.prev_pid)<br />
			nr_context_switch_bugs++;<br />
	}<br />
	if (trace_handler->switch_event)<br />
		trace_handler->switch_event(&#038;switch_event, event, this_cpu, timestamp, thread);</p>
<p>	curr_pid[this_cpu] = switch_event.next_pid;<br />
}</p>
<p>static void<br />
process_sched_runtime_event(struct raw_event_sample *raw,<br />
			   struct event *event,<br />
			   int cpu __used,<br />
			   u64 timestamp __used,<br />
			   struct thread *thread __used)<br />
{<br />
	struct trace_runtime_event runtime_event;</p>
<p>	FILL_ARRAY(runtime_event, comm, event, raw->data);<br />
	FILL_FIELD(runtime_event, pid, event, raw->data);<br />
	FILL_FIELD(runtime_event, runtime, event, raw->data);<br />
	FILL_FIELD(runtime_event, vruntime, event, raw->data);</p>
<p>	if (trace_handler->runtime_event)<br />
		trace_handler->runtime_event(&#038;runtime_event, event, cpu, timestamp, thread);<br />
}</p>
<p>static void<br />
process_sched_fork_event(struct raw_event_sample *raw,<br />
			 struct event *event,<br />
			 int cpu __used,<br />
			 u64 timestamp __used,<br />
			 struct thread *thread __used)<br />
{<br />
	struct trace_fork_event fork_event;</p>
<p>	FILL_COMMON_FIELDS(fork_event, event, raw->data);</p>
<p>	FILL_ARRAY(fork_event, parent_comm, event, raw->data);<br />
	FILL_FIELD(fork_event, parent_pid, event, raw->data);<br />
	FILL_ARRAY(fork_event, child_comm, event, raw->data);<br />
	FILL_FIELD(fork_event, child_pid, event, raw->data);</p>
<p>	if (trace_handler->fork_event)<br />
		trace_handler->fork_event(&#038;fork_event, event, cpu, timestamp, thread);<br />
}</p>
<p>static void<br />
process_sched_exit_event(struct event *event,<br />
			 int cpu __used,<br />
			 u64 timestamp __used,<br />
			 struct thread *thread __used)<br />
{<br />
	if (verbose)<br />
		printf(&raquo;sched_exit event %p\n&raquo;, event);<br />
}</p>
<p>static void<br />
process_raw_event(event_t *raw_event __used, void *more_data,<br />
		  int cpu, u64 timestamp, struct thread *thread)<br />
{<br />
	struct raw_event_sample *raw = more_data;<br />
	struct event *event;<br />
	int type;</p>
<p>	type = trace_parse_common_type(raw->data);<br />
	event = trace_find_event(type);</p>
<p>	if (!strcmp(event->name, &laquo;sched_switch&raquo;))<br />
		process_sched_switch_event(raw, event, cpu, timestamp, thread);<br />
	if (!strcmp(event->name, &laquo;sched_stat_runtime&raquo;))<br />
		process_sched_runtime_event(raw, event, cpu, timestamp, thread);<br />
	if (!strcmp(event->name, &laquo;sched_wakeup&raquo;))<br />
		process_sched_wakeup_event(raw, event, cpu, timestamp, thread);<br />
	if (!strcmp(event->name, &laquo;sched_wakeup_new&raquo;))<br />
		process_sched_wakeup_event(raw, event, cpu, timestamp, thread);<br />
	if (!strcmp(event->name, &laquo;sched_process_fork&raquo;))<br />
		process_sched_fork_event(raw, event, cpu, timestamp, thread);<br />
	if (!strcmp(event->name, &laquo;sched_process_exit&raquo;))<br />
		process_sched_exit_event(event, cpu, timestamp, thread);<br />
}</p>
<p>static int<br />
process_sample_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	char level;<br />
	int show = 0;<br />
	struct dso *dso = NULL;<br />
	struct thread *thread;<br />
	u64 ip = event->ip.ip;<br />
	u64 timestamp = -1;<br />
	u32 cpu = -1;<br />
	u64 period = 1;<br />
	void *more_data = event->ip.__more_data;<br />
	int cpumode;</p>
<p>	thread = threads__findnew(event->ip.pid, &#038;threads, &#038;last_match);</p>
<p>	if (sample_type &#038; PERF_SAMPLE_TIME) {<br />
		timestamp = *(u64 *)more_data;<br />
		more_data += sizeof(u64);<br />
	}</p>
<p>	if (sample_type &#038; PERF_SAMPLE_CPU) {<br />
		cpu = *(u32 *)more_data;<br />
		more_data += sizeof(u32);<br />
		more_data += sizeof(u32); /* reserved */<br />
	}</p>
<p>	if (sample_type &#038; PERF_SAMPLE_PERIOD) {<br />
		period = *(u64 *)more_data;<br />
		more_data += sizeof(u64);<br />
	}</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->header.misc,<br />
		event->ip.pid, event->ip.tid,<br />
		(void *)(long)ip,<br />
		(long long)period);</p>
<p>	dump_printf(&raquo; &#8230; thread: %s:%d\n&raquo;, thread->comm, thread->pid);</p>
<p>	if (thread == NULL) {<br />
		eprintf(&raquo;problem processing %d event, skipping it.\n&raquo;,<br />
			event->header.type);<br />
		return -1;<br />
	}</p>
<p>	cpumode = event->header.misc &#038; PERF_RECORD_MISC_CPUMODE_MASK;</p>
<p>	if (cpumode == PERF_RECORD_MISC_KERNEL) {<br />
		show = SHOW_KERNEL;<br />
		level = &#8216;k&#8217;;</p>
<p>		dso = kernel_dso;</p>
<p>		dump_printf(&raquo; &#8230;&#8230; dso: %s\n&raquo;, dso->name);</p>
<p>	} else if (cpumode == PERF_RECORD_MISC_USER) {</p>
<p>		show = SHOW_USER;<br />
		level = &#8216;.&#8217;;</p>
<p>	} else {<br />
		show = SHOW_HV;<br />
		level = &#8216;H&#8217;;</p>
<p>		dso = hypervisor_dso;</p>
<p>		dump_printf(&raquo; &#8230;&#8230; dso: [hypervisor]\n&raquo;);<br />
	}</p>
<p>	if (sample_type &#038; PERF_SAMPLE_RAW)<br />
		process_raw_event(event, more_data, cpu, timestamp, thread);</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	trace_event(event);</p>
<p>	nr_events++;<br />
	switch (event->header.type) {<br />
	case PERF_RECORD_MMAP:<br />
		return 0;<br />
	case PERF_RECORD_LOST:<br />
		nr_lost_chunks++;<br />
		nr_lost_events += event->lost.lost;<br />
		return 0;</p>
<p>	case PERF_RECORD_COMM:<br />
		return process_comm_event(event, offset, head);</p>
<p>	case PERF_RECORD_EXIT &#8230; PERF_RECORD_READ:<br />
		return 0;</p>
<p>	case PERF_RECORD_SAMPLE:<br />
		return process_sample_event(event, offset, head);</p>
<p>	case PERF_RECORD_MAX:<br />
	default:<br />
		return -1;<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static int read_events(void)<br />
{<br />
	int ret, rc = EXIT_FAILURE;<br />
	unsigned long offset = 0;<br />
	unsigned long head = 0;<br />
	struct stat perf_stat;<br />
	event_t *event;<br />
	uint32_t size;<br />
	char *buf;</p>
<p>	trace_report();<br />
	register_idle_thread(&#038;threads, &#038;last_match);</p>
<p>	input = open(input_name, O_RDONLY);<br />
	if (input < 0) {<br />
		perror("failed to open file");<br />
		exit(-1);<br />
	}</p>
<p>	ret = fstat(input, &#038;perf_stat);<br />
	if (ret < 0) {<br />
		perror("failed to stat file");<br />
		exit(-1);<br />
	}</p>
<p>	if (!perf_stat.st_size) {<br />
		fprintf(stderr, "zero-sized file, nothing to do!\n");<br />
		exit(0);<br />
	}<br />
	header = perf_header__read(input);<br />
	head = header->data_offset;<br />
	sample_type = perf_header__sample_type(header);</p>
<p>	if (!(sample_type &#038; PERF_SAMPLE_RAW))<br />
		die(&raquo;No trace sample to read. Did you call perf record &raquo;<br />
		    &laquo;without -R?&raquo;);</p>
<p>	if (load_kernel() < 0) {<br />
		perror("failed to load kernel symbols");<br />
		return EXIT_FAILURE;<br />
	}</p>
<p>remap:<br />
	buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,<br />
			   MAP_SHARED, input, offset);<br />
	if (buf == MAP_FAILED) {<br />
		perror("failed to mmap file");<br />
		exit(-1);<br />
	}</p>
<p>more:<br />
	event = (event_t *)(buf + head);</p>
<p>	size = event->header.size;<br />
	if (!size)<br />
		size = 8;</p>
<p>	if (head + event->header.size >= page_size * mmap_window) {<br />
		unsigned long shift = page_size * (head / page_size);<br />
		int res;</p>
<p>		res = munmap(buf, page_size * mmap_window);<br />
		assert(res == 0);</p>
<p>		offset += shift;<br />
		head -= shift;<br />
		goto remap;<br />
	}</p>
<p>	size = event->header.size;</p>
<p>	if (!size || process_event(event, offset, head) < 0) {</p>
<p>		/*<br />
		 * assume we lost track of the stream, check alignment, and<br />
		 * increment a single u64 in the hope to catch on again 'soon'.<br />
		 */</p>
<p>		if (unlikely(head &#038; 7))<br />
			head &#038;= ~7ULL;</p>
<p>		size = 8;<br />
	}</p>
<p>	head += size;</p>
<p>	if (offset + head < (unsigned long)perf_stat.st_size)<br />
		goto more;</p>
<p>	rc = EXIT_SUCCESS;<br />
	close(input);</p>
<p>	return rc;<br />
}</p>
<p>static void print_bad_events(void)<br />
{<br />
	if (nr_unordered_timestamps &#038;&#038; nr_timestamps) {<br />
		printf("  INFO: %.3f%% unordered timestamps (%ld out of %ld)\n",<br />
			(double)nr_unordered_timestamps/(double)nr_timestamps*100.0,<br />
			nr_unordered_timestamps, nr_timestamps);<br />
	}<br />
	if (nr_lost_events &#038;&#038; nr_events) {<br />
		printf("  INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",<br />
			(double)nr_lost_events/(double)nr_events*100.0,<br />
			nr_lost_events, nr_events, nr_lost_chunks);<br />
	}<br />
	if (nr_state_machine_bugs &#038;&#038; nr_timestamps) {<br />
		printf("  INFO: %.3f%% state machine bugs (%ld out of %ld)",<br />
			(double)nr_state_machine_bugs/(double)nr_timestamps*100.0,<br />
			nr_state_machine_bugs, nr_timestamps);<br />
		if (nr_lost_events)<br />
			printf(" (due to lost events?)");<br />
		printf("\n");<br />
	}<br />
	if (nr_context_switch_bugs &#038;&#038; nr_timestamps) {<br />
		printf("  INFO: %.3f%% context switch bugs (%ld out of %ld)",<br />
			(double)nr_context_switch_bugs/(double)nr_timestamps*100.0,<br />
			nr_context_switch_bugs, nr_timestamps);<br />
		if (nr_lost_events)<br />
			printf(" (due to lost events?)");<br />
		printf("\n");<br />
	}<br />
}</p>
<p>static void __cmd_lat(void)<br />
{<br />
	struct rb_node *next;</p>
<p>	setup_pager();<br />
	read_events();<br />
	sort_lat();</p>
<p>	printf("\n -----------------------------------------------------------------------------------------\n");<br />
	printf("  Task                  |   Runtime ms  | Switches | Average delay ms | Maximum delay ms |\n");<br />
	printf(" -----------------------------------------------------------------------------------------\n");</p>
<p>	next = rb_first(&#038;sorted_atom_root);</p>
<p>	while (next) {<br />
		struct work_atoms *work_list;</p>
<p>		work_list = rb_entry(next, struct work_atoms, node);<br />
		output_lat_thread(work_list);<br />
		next = rb_next(next);<br />
	}</p>
<p>	printf(" -----------------------------------------------------------------------------------------\n");<br />
	printf("  TOTAL:                |%11.3f ms |%9Ld |\n",<br />
		(double)all_runtime/1e6, all_count);</p>
<p>	printf(" ---------------------------------------------------\n");</p>
<p>	print_bad_events();<br />
	printf("\n");</p>
<p>}</p>
<p>static struct trace_sched_handler map_ops  = {<br />
	.wakeup_event		= NULL,<br />
	.switch_event		= map_switch_event,<br />
	.runtime_event		= NULL,<br />
	.fork_event		= NULL,<br />
};</p>
<p>static void __cmd_map(void)<br />
{<br />
	max_cpu = sysconf(_SC_NPROCESSORS_CONF);</p>
<p>	setup_pager();<br />
	read_events();<br />
	print_bad_events();<br />
}</p>
<p>static void __cmd_replay(void)<br />
{<br />
	unsigned long i;</p>
<p>	calibrate_run_measurement_overhead();<br />
	calibrate_sleep_measurement_overhead();</p>
<p>	test_calibrations();</p>
<p>	read_events();</p>
<p>	printf("nr_run_events:        %ld\n", nr_run_events);<br />
	printf("nr_sleep_events:      %ld\n", nr_sleep_events);<br />
	printf("nr_wakeup_events:     %ld\n", nr_wakeup_events);</p>
<p>	if (targetless_wakeups)<br />
		printf("target-less wakeups:  %ld\n", targetless_wakeups);<br />
	if (multitarget_wakeups)<br />
		printf("multi-target wakeups: %ld\n", multitarget_wakeups);<br />
	if (nr_run_events_optimized)<br />
		printf("run atoms optimized: %ld\n",<br />
			nr_run_events_optimized);</p>
<p>	print_task_traces();<br />
	add_cross_task_wakeups();</p>
<p>	create_tasks();<br />
	printf("------------------------------------------------------------\n");<br />
	for (i = 0; i < replay_repeat; i++)<br />
		run_one_test();<br />
}</p>
<p>static const char * const sched_usage[] = {<br />
	"perf sched [<options>] {record|latency|map|replay|trace}&raquo;,<br />
	NULL<br />
};</p>
<p>static const struct option sched_options[] = {<br />
	OPT_STRING(&#8217;i', &laquo;input&raquo;, &#038;input_name, &laquo;file&raquo;,<br />
		    &laquo;input file name&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show symbol address, etc)&raquo;),<br />
	OPT_BOOLEAN(&#8217;D', &laquo;dump-raw-trace&raquo;, &#038;dump_trace,<br />
		    &laquo;dump raw trace in ASCII&raquo;),<br />
	OPT_END()<br />
};</p>
<p>static const char * const latency_usage[] = {<br />
	&laquo;perf sched latency [<options>]&laquo;,<br />
	NULL<br />
};</p>
<p>static const struct option latency_options[] = {<br />
	OPT_STRING(&#8217;s&#8217;, &laquo;sort&raquo;, &#038;sort_order, &laquo;key[,key2...]&laquo;,<br />
		   &laquo;sort by key(s): runtime, switch, avg, max&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show symbol address, etc)&raquo;),<br />
	OPT_BOOLEAN(&#8217;D', &laquo;dump-raw-trace&raquo;, &#038;dump_trace,<br />
		    &laquo;dump raw trace in ASCII&raquo;),<br />
	OPT_END()<br />
};</p>
<p>static const char * const replay_usage[] = {<br />
	&laquo;perf sched replay [<options>]&laquo;,<br />
	NULL<br />
};</p>
<p>static const struct option replay_options[] = {<br />
	OPT_INTEGER(&#8217;r', &laquo;repeat&raquo;, &#038;replay_repeat,<br />
		    &laquo;repeat the workload replay N times (-1: infinite)&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show symbol address, etc)&raquo;),<br />
	OPT_BOOLEAN(&#8217;D', &laquo;dump-raw-trace&raquo;, &#038;dump_trace,<br />
		    &laquo;dump raw trace in ASCII&raquo;),<br />
	OPT_END()<br />
};</p>
<p>static void setup_sorting(void)<br />
{<br />
	char *tmp, *tok, *str = strdup(sort_order);</p>
<p>	for (tok = strtok_r(str, &laquo;, &laquo;, &#038;tmp);<br />
			tok; tok = strtok_r(NULL, &laquo;, &laquo;, &#038;tmp)) {<br />
		if (sort_dimension__add(tok, &#038;sort_list) < 0) {<br />
			error("Unknown --sort key: `%s'", tok);<br />
			usage_with_options(latency_usage, latency_options);<br />
		}<br />
	}</p>
<p>	free(str);</p>
<p>	sort_dimension__add("pid", &#038;cmp_pid);<br />
}</p>
<p>static const char *record_args[] = {<br />
	"record",<br />
	"-a",<br />
	"-R",<br />
	"-M",<br />
	"-f",<br />
	"-m", "1024",<br />
	"-c", "1",<br />
	"-e", "sched:sched_switch:r",<br />
	"-e", "sched:sched_stat_wait:r",<br />
	"-e", "sched:sched_stat_sleep:r",<br />
	"-e", "sched:sched_stat_iowait:r",<br />
	"-e", "sched:sched_stat_runtime:r",<br />
	"-e", "sched:sched_process_exit:r",<br />
	"-e", "sched:sched_process_fork:r",<br />
	"-e", "sched:sched_wakeup:r",<br />
	"-e", "sched:sched_migrate_task:r",<br />
};</p>
<p>static int __cmd_record(int argc, const char **argv)<br />
{<br />
	unsigned int rec_argc, i, j;<br />
	const char **rec_argv;</p>
<p>	rec_argc = ARRAY_SIZE(record_args) + argc - 1;<br />
	rec_argv = calloc(rec_argc + 1, sizeof(char *));</p>
<p>	for (i = 0; i < ARRAY_SIZE(record_args); i++)<br />
		rec_argv[i] = strdup(record_args[i]);</p>
<p>	for (j = 1; j < (unsigned int)argc; j++, i++)<br />
		rec_argv[i] = argv[j];</p>
<p>	BUG_ON(i != rec_argc);</p>
<p>	return cmd_record(i, rec_argv, NULL);<br />
}</p>
<p>int cmd_sched(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	symbol__init();<br />
	page_size = getpagesize();</p>
<p>	argc = parse_options(argc, argv, sched_options, sched_usage,<br />
			     PARSE_OPT_STOP_AT_NON_OPTION);<br />
	if (!argc)<br />
		usage_with_options(sched_usage, sched_options);</p>
<p>	if (!strncmp(argv[0], "rec", 3)) {<br />
		return __cmd_record(argc, argv);<br />
	} else if (!strncmp(argv[0], "lat", 3)) {<br />
		trace_handler = &#038;lat_ops;<br />
		if (argc > 1) {<br />
			argc = parse_options(argc, argv, latency_options, latency_usage, 0);<br />
			if (argc)<br />
				usage_with_options(latency_usage, latency_options);<br />
		}<br />
		setup_sorting();<br />
		__cmd_lat();<br />
	} else if (!strcmp(argv[0], &laquo;map&raquo;)) {<br />
		trace_handler = &#038;map_ops;<br />
		setup_sorting();<br />
		__cmd_map();<br />
	} else if (!strncmp(argv[0], &laquo;rep&raquo;, 3)) {<br />
		trace_handler = &#038;replay_ops;<br />
		if (argc) {<br />
			argc = parse_options(argc, argv, replay_options, replay_usage, 0);<br />
			if (argc)<br />
				usage_with_options(replay_usage, replay_options);<br />
		}<br />
		__cmd_replay();<br />
	} else if (!strcmp(argv[0], &laquo;trace&raquo;)) {<br />
		/*<br />
		 * Aliased to &#8216;perf trace&#8217; for now:<br />
		 */<br />
		return cmd_trace(argc, argv, prefix);<br />
	} else {<br />
		usage_with_options(sched_usage, sched_options);<br />
	}</p>
<p>	return 0;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-sched-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-report.c</title>
		<link>http://lynyrd.ru/builtin-report-c</link>
		<comments>http://lynyrd.ru/builtin-report-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:26:54 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=603</guid>
		<description><![CDATA[/*
 * builtin-report.c
 *
 * Builtin report command: Analyze the perf.data input file,
 * look up and read DSOs and symbol information and display
 * a histogram of results, along various sorting keys.
 */
#include &#171;builtin.h&#187;
#include &#171;util/util.h&#187;
#include &#171;util/color.h&#187;
#include

#include &#171;util/cache.h&#187;
#include

#include &#171;util/symbol.h&#187;
#include &#171;util/string.h&#187;
#include &#171;util/callchain.h&#187;
#include &#171;util/strlist.h&#187;
#include &#171;util/values.h&#187;
#include &#171;perf.h&#187;
#include &#171;util/debug.h&#187;
#include &#171;util/header.h&#187;
#include &#171;util/parse-options.h&#187;
#include &#171;util/parse-events.h&#187;
#include &#171;util/thread.h&#187;
static char		const *input_name = &#171;perf.data&#187;;
static char		default_sort_order[] = ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-report.c<span id="more-603"></span><br />
 *<br />
 * Builtin report command: Analyze the perf.data input file,<br />
 * look up and read DSOs and symbol information and display<br />
 * a histogram of results, along various sorting keys.<br />
 */<br />
#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;util/util.h&raquo;</p>
<p>#include &laquo;util/color.h&raquo;<br />
#include
<linux/list.h>
#include &laquo;util/cache.h&raquo;<br />
#include
<linux/rbtree.h>
#include &laquo;util/symbol.h&raquo;<br />
#include &laquo;util/string.h&raquo;<br />
#include &laquo;util/callchain.h&raquo;<br />
#include &laquo;util/strlist.h&raquo;<br />
#include &laquo;util/values.h&raquo;</p>
<p>#include &laquo;perf.h&raquo;<br />
#include &laquo;util/debug.h&raquo;<br />
#include &laquo;util/header.h&raquo;</p>
<p>#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;</p>
<p>#include &laquo;util/thread.h&raquo;</p>
<p>static char		const *input_name = &laquo;perf.data&raquo;;</p>
<p>static char		default_sort_order[] = &laquo;comm,dso,symbol&raquo;;<br />
static char		*sort_order = default_sort_order;<br />
static char		*dso_list_str, *comm_list_str, *sym_list_str,<br />
			*col_width_list_str;<br />
static struct strlist	*dso_list, *comm_list, *sym_list;<br />
static char		*field_sep;</p>
<p>static int		force;<br />
static int		input;<br />
static int		show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;</p>
<p>static int		full_paths;<br />
static int		show_nr_samples;</p>
<p>static int		show_threads;<br />
static struct perf_read_values	show_threads_values;</p>
<p>static char		default_pretty_printing_style[] = &laquo;normal&raquo;;<br />
static char		*pretty_printing_style = default_pretty_printing_style;</p>
<p>static unsigned long	page_size;<br />
static unsigned long	mmap_window = 32;</p>
<p>static char		default_parent_pattern[] = &laquo;^sys_|^do_page_fault&raquo;;<br />
static char		*parent_pattern = default_parent_pattern;<br />
static regex_t		parent_regex;</p>
<p>static int		exclude_other = 1;</p>
<p>static char		callchain_default_opt[] = &laquo;fractal,0.5&#8243;;</p>
<p>static int		callchain;</p>
<p>static char		__cwd[PATH_MAX];<br />
static char		*cwd = __cwd;<br />
static int		cwdlen;</p>
<p>static struct rb_root	threads;<br />
static struct thread	*last_match;</p>
<p>static struct perf_header *header;</p>
<p>static<br />
struct callchain_param	callchain_param = {<br />
	.mode	= CHAIN_GRAPH_REL,<br />
	.min_percent = 0.5<br />
};</p>
<p>static u64		sample_type;</p>
<p>static int repsep_fprintf(FILE *fp, const char *fmt, &#8230;)<br />
{<br />
	int n;<br />
	va_list ap;</p>
<p>	va_start(ap, fmt);<br />
	if (!field_sep)<br />
		n = vfprintf(fp, fmt, ap);<br />
	else {<br />
		char *bf = NULL;<br />
		n = vasprintf(&#038;bf, fmt, ap);<br />
		if (n > 0) {<br />
			char *sep = bf;</p>
<p>			while (1) {<br />
				sep = strchr(sep, *field_sep);<br />
				if (sep == NULL)<br />
					break;<br />
				*sep = &#8216;.&#8217;;<br />
			}<br />
		}<br />
		fputs(bf, fp);<br />
		free(bf);<br />
	}<br />
	va_end(ap);<br />
	return n;<br />
}</p>
<p>static unsigned int dsos__col_width,<br />
		    comms__col_width,<br />
		    threads__col_width;</p>
<p>/*<br />
 * histogram, sorted on item, collects counts<br />
 */</p>
<p>static struct rb_root hist;</p>
<p>struct hist_entry {<br />
	struct rb_node		rb_node;</p>
<p>	struct thread		*thread;<br />
	struct map		*map;<br />
	struct dso		*dso;<br />
	struct symbol		*sym;<br />
	struct symbol		*parent;<br />
	u64			ip;<br />
	char			level;<br />
	struct callchain_node	callchain;<br />
	struct rb_root		sorted_chain;</p>
<p>	u64			count;<br />
};</p>
<p>/*<br />
 * configurable sorting bits<br />
 */</p>
<p>struct sort_entry {<br />
	struct list_head list;</p>
<p>	const char *header;</p>
<p>	int64_t (*cmp)(struct hist_entry *, struct hist_entry *);<br />
	int64_t (*collapse)(struct hist_entry *, struct hist_entry *);<br />
	size_t	(*print)(FILE *fp, struct hist_entry *, unsigned int width);<br />
	unsigned int *width;<br />
	bool	elide;<br />
};</p>
<p>static int64_t cmp_null(void *l, void *r)<br />
{<br />
	if (!l &#038;&#038; !r)<br />
		return 0;<br />
	else if (!l)<br />
		return -1;<br />
	else<br />
		return 1;<br />
}</p>
<p>/* &#8211;sort pid */</p>
<p>static int64_t<br />
sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	return right->thread->pid &#8211; left->thread->pid;<br />
}</p>
<p>static size_t<br />
sort__thread_print(FILE *fp, struct hist_entry *self, unsigned int width)<br />
{<br />
	return repsep_fprintf(fp, &laquo;%*s:%5d&raquo;, width &#8211; 6,<br />
			      self->thread->comm ?: &laquo;&raquo;, self->thread->pid);<br />
}</p>
<p>static struct sort_entry sort_thread = {<br />
	.header = &laquo;Command:  Pid&raquo;,<br />
	.cmp	= sort__thread_cmp,<br />
	.print	= sort__thread_print,<br />
	.width	= &#038;threads__col_width,<br />
};</p>
<p>/* &#8211;sort comm */</p>
<p>static int64_t<br />
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	return right->thread->pid &#8211; left->thread->pid;<br />
}</p>
<p>static int64_t<br />
sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	char *comm_l = left->thread->comm;<br />
	char *comm_r = right->thread->comm;</p>
<p>	if (!comm_l || !comm_r)<br />
		return cmp_null(comm_l, comm_r);</p>
<p>	return strcmp(comm_l, comm_r);<br />
}</p>
<p>static size_t<br />
sort__comm_print(FILE *fp, struct hist_entry *self, unsigned int width)<br />
{<br />
	return repsep_fprintf(fp, &laquo;%*s&raquo;, width, self->thread->comm);<br />
}</p>
<p>static struct sort_entry sort_comm = {<br />
	.header		= &laquo;Command&raquo;,<br />
	.cmp		= sort__comm_cmp,<br />
	.collapse	= sort__comm_collapse,<br />
	.print		= sort__comm_print,<br />
	.width		= &#038;comms__col_width,<br />
};</p>
<p>/* &#8211;sort dso */</p>
<p>static int64_t<br />
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct dso *dso_l = left->dso;<br />
	struct dso *dso_r = right->dso;</p>
<p>	if (!dso_l || !dso_r)<br />
		return cmp_null(dso_l, dso_r);</p>
<p>	return strcmp(dso_l->name, dso_r->name);<br />
}</p>
<p>static size_t<br />
sort__dso_print(FILE *fp, struct hist_entry *self, unsigned int width)<br />
{<br />
	if (self->dso)<br />
		return repsep_fprintf(fp, &laquo;%-*s&raquo;, width, self->dso->name);</p>
<p>	return repsep_fprintf(fp, &laquo;%*llx&raquo;, width, (u64)self->ip);<br />
}</p>
<p>static struct sort_entry sort_dso = {<br />
	.header = &laquo;Shared Object&raquo;,<br />
	.cmp	= sort__dso_cmp,<br />
	.print	= sort__dso_print,<br />
	.width	= &#038;dsos__col_width,<br />
};</p>
<p>/* &#8211;sort symbol */</p>
<p>static int64_t<br />
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	u64 ip_l, ip_r;</p>
<p>	if (left->sym == right->sym)<br />
		return 0;</p>
<p>	ip_l = left->sym ? left->sym->start : left->ip;<br />
	ip_r = right->sym ? right->sym->start : right->ip;</p>
<p>	return (int64_t)(ip_r &#8211; ip_l);<br />
}</p>
<p>static size_t<br />
sort__sym_print(FILE *fp, struct hist_entry *self, unsigned int width __used)<br />
{<br />
	size_t ret = 0;</p>
<p>	if (verbose)<br />
		ret += repsep_fprintf(fp, &laquo;%#018llx %c &laquo;, (u64)self->ip,<br />
				      dso__symtab_origin(self->dso));</p>
<p>	ret += repsep_fprintf(fp, &laquo;[%c] &laquo;, self->level);<br />
	if (self->sym) {<br />
		ret += repsep_fprintf(fp, &laquo;%s&raquo;, self->sym->name);</p>
<p>		if (self->sym->module)<br />
			ret += repsep_fprintf(fp, &laquo;\t[%s]&laquo;,<br />
					     self->sym->module->name);<br />
	} else {<br />
		ret += repsep_fprintf(fp, &laquo;%#016llx&raquo;, (u64)self->ip);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static struct sort_entry sort_sym = {<br />
	.header = &laquo;Symbol&raquo;,<br />
	.cmp	= sort__sym_cmp,<br />
	.print	= sort__sym_print,<br />
};</p>
<p>/* &#8211;sort parent */</p>
<p>static int64_t<br />
sort__parent_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct symbol *sym_l = left->parent;<br />
	struct symbol *sym_r = right->parent;</p>
<p>	if (!sym_l || !sym_r)<br />
		return cmp_null(sym_l, sym_r);</p>
<p>	return strcmp(sym_l->name, sym_r->name);<br />
}</p>
<p>static size_t<br />
sort__parent_print(FILE *fp, struct hist_entry *self, unsigned int width)<br />
{<br />
	return repsep_fprintf(fp, &laquo;%-*s&raquo;, width,<br />
			      self->parent ? self->parent->name : &laquo;[other]&laquo;);<br />
}</p>
<p>static unsigned int parent_symbol__col_width;</p>
<p>static struct sort_entry sort_parent = {<br />
	.header = &laquo;Parent symbol&raquo;,<br />
	.cmp	= sort__parent_cmp,<br />
	.print	= sort__parent_print,<br />
	.width	= &#038;parent_symbol__col_width,<br />
};</p>
<p>static int sort__need_collapse = 0;<br />
static int sort__has_parent = 0;</p>
<p>struct sort_dimension {<br />
	const char		*name;<br />
	struct sort_entry	*entry;<br />
	int			taken;<br />
};</p>
<p>static struct sort_dimension sort_dimensions[] = {<br />
	{ .name = &laquo;pid&raquo;,	.entry = &#038;sort_thread,	},<br />
	{ .name = &laquo;comm&raquo;,	.entry = &#038;sort_comm,	},<br />
	{ .name = &laquo;dso&raquo;,	.entry = &#038;sort_dso,	},<br />
	{ .name = &laquo;symbol&raquo;,	.entry = &#038;sort_sym,	},<br />
	{ .name = &laquo;parent&raquo;,	.entry = &#038;sort_parent,	},<br />
};</p>
<p>static LIST_HEAD(hist_entry__sort_list);</p>
<p>static int sort_dimension__add(const char *tok)<br />
{<br />
	unsigned int i;</p>
<p>	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {<br />
		struct sort_dimension *sd = &#038;sort_dimensions[i];</p>
<p>		if (sd->taken)<br />
			continue;</p>
<p>		if (strncasecmp(tok, sd->name, strlen(tok)))<br />
			continue;</p>
<p>		if (sd->entry->collapse)<br />
			sort__need_collapse = 1;</p>
<p>		if (sd->entry == &#038;sort_parent) {<br />
			int ret = regcomp(&#038;parent_regex, parent_pattern, REG_EXTENDED);<br />
			if (ret) {<br />
				char err[BUFSIZ];</p>
<p>				regerror(ret, &#038;parent_regex, err, sizeof(err));<br />
				fprintf(stderr, &laquo;Invalid regex: %s\n%s&raquo;,<br />
					parent_pattern, err);<br />
				exit(-1);<br />
			}<br />
			sort__has_parent = 1;<br />
		}</p>
<p>		list_add_tail(&#038;sd->entry->list, &#038;hist_entry__sort_list);<br />
		sd->taken = 1;</p>
<p>		return 0;<br />
	}</p>
<p>	return -ESRCH;<br />
}</p>
<p>static int64_t<br />
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct sort_entry *se;<br />
	int64_t cmp = 0;</p>
<p>	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		cmp = se->cmp(left, right);<br />
		if (cmp)<br />
			break;<br />
	}</p>
<p>	return cmp;<br />
}</p>
<p>static int64_t<br />
hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct sort_entry *se;<br />
	int64_t cmp = 0;</p>
<p>	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		int64_t (*f)(struct hist_entry *, struct hist_entry *);</p>
<p>		f = se->collapse ?: se->cmp;</p>
<p>		cmp = f(left, right);<br />
		if (cmp)<br />
			break;<br />
	}</p>
<p>	return cmp;<br />
}</p>
<p>static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask)<br />
{<br />
	int i;<br />
	size_t ret = 0;</p>
<p>	ret += fprintf(fp, &laquo;%s&raquo;, &raquo;                &laquo;);</p>
<p>	for (i = 0; i < depth; i++)<br />
		if (depth_mask &#038; (1 << i))<br />
			ret += fprintf(fp, "|          ");<br />
		else<br />
			ret += fprintf(fp, "           ");</p>
<p>	ret += fprintf(fp, "\n");</p>
<p>	return ret;<br />
}<br />
static size_t<br />
ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain, int depth,<br />
		       int depth_mask, int count, u64 total_samples,<br />
		       int hits)<br />
{<br />
	int i;<br />
	size_t ret = 0;</p>
<p>	ret += fprintf(fp, "%s", "                ");<br />
	for (i = 0; i < depth; i++) {<br />
		if (depth_mask &#038; (1 << i))<br />
			ret += fprintf(fp, "|");<br />
		else<br />
			ret += fprintf(fp, " ");<br />
		if (!count &#038;&#038; i == depth - 1) {<br />
			double percent;</p>
<p>			percent = hits * 100.0 / total_samples;<br />
			ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);<br />
		} else<br />
			ret += fprintf(fp, "%s", "          ");<br />
	}<br />
	if (chain->sym)<br />
		ret += fprintf(fp, &laquo;%s\n&raquo;, chain->sym->name);<br />
	else<br />
		ret += fprintf(fp, &laquo;%p\n&raquo;, (void *)(long)chain->ip);</p>
<p>	return ret;<br />
}</p>
<p>static struct symbol *rem_sq_bracket;<br />
static struct callchain_list rem_hits;</p>
<p>static void init_rem_hits(void)<br />
{<br />
	rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);<br />
	if (!rem_sq_bracket) {<br />
		fprintf(stderr, &laquo;Not enough memory to display remaining hits\n&raquo;);<br />
		return;<br />
	}</p>
<p>	strcpy(rem_sq_bracket->name, &laquo;[...]&laquo;);<br />
	rem_hits.sym = rem_sq_bracket;<br />
}</p>
<p>static size_t<br />
callchain__fprintf_graph(FILE *fp, struct callchain_node *self,<br />
			u64 total_samples, int depth, int depth_mask)<br />
{<br />
	struct rb_node *node, *next;<br />
	struct callchain_node *child;<br />
	struct callchain_list *chain;<br />
	int new_depth_mask = depth_mask;<br />
	u64 new_total;<br />
	u64 remaining;<br />
	size_t ret = 0;<br />
	int i;</p>
<p>	if (callchain_param.mode == CHAIN_GRAPH_REL)<br />
		new_total = self->children_hit;<br />
	else<br />
		new_total = total_samples;</p>
<p>	remaining = new_total;</p>
<p>	node = rb_first(&#038;self->rb_root);<br />
	while (node) {<br />
		u64 cumul;</p>
<p>		child = rb_entry(node, struct callchain_node, rb_node);<br />
		cumul = cumul_hits(child);<br />
		remaining -= cumul;</p>
<p>		/*<br />
		 * The depth mask manages the output of pipes that show<br />
		 * the depth. We don&#8217;t want to keep the pipes of the current<br />
		 * level for the last child of this depth.<br />
		 * Except if we have remaining filtered hits. They will<br />
		 * supersede the last child<br />
		 */<br />
		next = rb_next(node);<br />
		if (!next &#038;&#038; (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))<br />
			new_depth_mask &#038;= ~(1 << (depth - 1));</p>
<p>		/*<br />
		 * But we keep the older depth mask for the line seperator<br />
		 * to keep the level link until we reach the last child<br />
		 */<br />
		ret += ipchain__fprintf_graph_line(fp, depth, depth_mask);<br />
		i = 0;<br />
		list_for_each_entry(chain, &#038;child->val, list) {<br />
			if (chain->ip >= PERF_CONTEXT_MAX)<br />
				continue;<br />
			ret += ipchain__fprintf_graph(fp, chain, depth,<br />
						      new_depth_mask, i++,<br />
						      new_total,<br />
						      cumul);<br />
		}<br />
		ret += callchain__fprintf_graph(fp, child, new_total,<br />
						depth + 1,<br />
						new_depth_mask | (1 << depth));<br />
		node = next;<br />
	}</p>
<p>	if (callchain_param.mode == CHAIN_GRAPH_REL &#038;&#038;<br />
		remaining &#038;&#038; remaining != new_total) {</p>
<p>		if (!rem_sq_bracket)<br />
			return ret;</p>
<p>		new_depth_mask &#038;= ~(1 << (depth - 1));</p>
<p>		ret += ipchain__fprintf_graph(fp, &#038;rem_hits, depth,<br />
					      new_depth_mask, 0, new_total,<br />
					      remaining);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static size_t<br />
callchain__fprintf_flat(FILE *fp, struct callchain_node *self,<br />
			u64 total_samples)<br />
{<br />
	struct callchain_list *chain;<br />
	size_t ret = 0;</p>
<p>	if (!self)<br />
		return 0;</p>
<p>	ret += callchain__fprintf_flat(fp, self->parent, total_samples);</p>
<p>	list_for_each_entry(chain, &#038;self->val, list) {<br />
		if (chain->ip >= PERF_CONTEXT_MAX)<br />
			continue;<br />
		if (chain->sym)<br />
			ret += fprintf(fp, &raquo;                %s\n&raquo;, chain->sym->name);<br />
		else<br />
			ret += fprintf(fp, &raquo;                %p\n&raquo;,<br />
					(void *)(long)chain->ip);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static size_t<br />
hist_entry_callchain__fprintf(FILE *fp, struct hist_entry *self,<br />
			      u64 total_samples)<br />
{<br />
	struct rb_node *rb_node;<br />
	struct callchain_node *chain;<br />
	size_t ret = 0;</p>
<p>	rb_node = rb_first(&#038;self->sorted_chain);<br />
	while (rb_node) {<br />
		double percent;</p>
<p>		chain = rb_entry(rb_node, struct callchain_node, rb_node);<br />
		percent = chain->hit * 100.0 / total_samples;<br />
		switch (callchain_param.mode) {<br />
		case CHAIN_FLAT:<br />
			ret += percent_color_fprintf(fp, &raquo;           %6.2f%%\n&raquo;,<br />
						     percent);<br />
			ret += callchain__fprintf_flat(fp, chain, total_samples);<br />
			break;<br />
		case CHAIN_GRAPH_ABS: /* Falldown */<br />
		case CHAIN_GRAPH_REL:<br />
			ret += callchain__fprintf_graph(fp, chain,<br />
							total_samples, 1, 1);<br />
		case CHAIN_NONE:<br />
		default:<br />
			break;<br />
		}<br />
		ret += fprintf(fp, &laquo;\n&raquo;);<br />
		rb_node = rb_next(rb_node);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static size_t<br />
hist_entry__fprintf(FILE *fp, struct hist_entry *self, u64 total_samples)<br />
{<br />
	struct sort_entry *se;<br />
	size_t ret;</p>
<p>	if (exclude_other &#038;&#038; !self->parent)<br />
		return 0;</p>
<p>	if (total_samples)<br />
		ret = percent_color_fprintf(fp,<br />
					    field_sep ? &laquo;%.2f&raquo; : &raquo;   %6.2f%%&raquo;,<br />
					(self->count * 100.0) / total_samples);<br />
	else<br />
		ret = fprintf(fp, field_sep ? &laquo;%lld&raquo; : &laquo;%12lld &laquo;, self->count);</p>
<p>	if (show_nr_samples) {<br />
		if (field_sep)<br />
			fprintf(fp, &laquo;%c%lld&raquo;, *field_sep, self->count);<br />
		else<br />
			fprintf(fp, &laquo;%11lld&raquo;, self->count);<br />
	}</p>
<p>	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		if (se->elide)<br />
			continue;</p>
<p>		fprintf(fp, &laquo;%s&raquo;, field_sep ?: &raquo;  &laquo;);<br />
		ret += se->print(fp, self, se->width ? *se->width : 0);<br />
	}</p>
<p>	ret += fprintf(fp, &laquo;\n&raquo;);</p>
<p>	if (callchain)<br />
		hist_entry_callchain__fprintf(fp, self, total_samples);</p>
<p>	return ret;<br />
}</p>
<p>/*<br />
 *<br />
 */</p>
<p>static void dso__calc_col_width(struct dso *self)<br />
{<br />
	if (!col_width_list_str &#038;&#038; !field_sep &#038;&#038;<br />
	    (!dso_list || strlist__has_entry(dso_list, self->name))) {<br />
		unsigned int slen = strlen(self->name);<br />
		if (slen > dsos__col_width)<br />
			dsos__col_width = slen;<br />
	}</p>
<p>	self->slen_calculated = 1;<br />
}</p>
<p>static void thread__comm_adjust(struct thread *self)<br />
{<br />
	char *comm = self->comm;</p>
<p>	if (!col_width_list_str &#038;&#038; !field_sep &#038;&#038;<br />
	    (!comm_list || strlist__has_entry(comm_list, comm))) {<br />
		unsigned int slen = strlen(comm);</p>
<p>		if (slen > comms__col_width) {<br />
			comms__col_width = slen;<br />
			threads__col_width = slen + 6;<br />
		}<br />
	}<br />
}</p>
<p>static int thread__set_comm_adjust(struct thread *self, const char *comm)<br />
{<br />
	int ret = thread__set_comm(self, comm);</p>
<p>	if (ret)<br />
		return ret;</p>
<p>	thread__comm_adjust(self);</p>
<p>	return 0;<br />
}</p>
<p>static struct symbol *<br />
resolve_symbol(struct thread *thread, struct map **mapp,<br />
	       struct dso **dsop, u64 *ipp)<br />
{<br />
	struct dso *dso = dsop ? *dsop : NULL;<br />
	struct map *map = mapp ? *mapp : NULL;<br />
	u64 ip = *ipp;</p>
<p>	if (!thread)<br />
		return NULL;</p>
<p>	if (dso)<br />
		goto got_dso;</p>
<p>	if (map)<br />
		goto got_map;</p>
<p>	map = thread__find_map(thread, ip);<br />
	if (map != NULL) {<br />
		/*<br />
		 * We have to do this here as we may have a dso<br />
		 * with no symbol hit that has a name longer than<br />
		 * the ones with symbols sampled.<br />
		 */<br />
		if (!sort_dso.elide &#038;&#038; !map->dso->slen_calculated)<br />
			dso__calc_col_width(map->dso);</p>
<p>		if (mapp)<br />
			*mapp = map;<br />
got_map:<br />
		ip = map->map_ip(map, ip);</p>
<p>		dso = map->dso;<br />
	} else {<br />
		/*<br />
		 * If this is outside of all known maps,<br />
		 * and is a negative address, try to look it<br />
		 * up in the kernel dso, as it might be a<br />
		 * vsyscall (which executes in user-mode):<br />
		 */<br />
		if ((long long)ip < 0)<br />
		dso = kernel_dso;<br />
	}<br />
	dump_printf(" ...... dso: %s\n", dso ? dso->name : &laquo;<not found>&laquo;);<br />
	dump_printf(&raquo; &#8230;&#8230; map: %Lx -> %Lx\n&raquo;, *ipp, ip);<br />
	*ipp  = ip;</p>
<p>	if (dsop)<br />
		*dsop = dso;</p>
<p>	if (!dso)<br />
		return NULL;<br />
got_dso:<br />
	return dso->find_symbol(dso, ip);<br />
}</p>
<p>static int call__match(struct symbol *sym)<br />
{<br />
	if (sym->name &#038;&#038; !regexec(&#038;parent_regex, sym->name, 0, NULL, 0))<br />
		return 1;</p>
<p>	return 0;<br />
}</p>
<p>static struct symbol **<br />
resolve_callchain(struct thread *thread, struct map *map __used,<br />
		    struct ip_callchain *chain, struct hist_entry *entry)<br />
{<br />
	u64 context = PERF_CONTEXT_MAX;<br />
	struct symbol **syms = NULL;<br />
	unsigned int i;</p>
<p>	if (callchain) {<br />
		syms = calloc(chain->nr, sizeof(*syms));<br />
		if (!syms) {<br />
			fprintf(stderr, &laquo;Can&#8217;t allocate memory for symbols\n&raquo;);<br />
			exit(-1);<br />
		}<br />
	}</p>
<p>	for (i = 0; i < chain->nr; i++) {<br />
		u64 ip = chain->ips[i];<br />
		struct dso *dso = NULL;<br />
		struct symbol *sym;</p>
<p>		if (ip >= PERF_CONTEXT_MAX) {<br />
			context = ip;<br />
			continue;<br />
		}</p>
<p>		switch (context) {<br />
		case PERF_CONTEXT_HV:<br />
			dso = hypervisor_dso;<br />
			break;<br />
		case PERF_CONTEXT_KERNEL:<br />
			dso = kernel_dso;<br />
			break;<br />
		default:<br />
			break;<br />
		}</p>
<p>		sym = resolve_symbol(thread, NULL, &#038;dso, &#038;ip);</p>
<p>		if (sym) {<br />
			if (sort__has_parent &#038;&#038; call__match(sym) &#038;&#038;<br />
			    !entry->parent)<br />
				entry->parent = sym;<br />
			if (!callchain)<br />
				break;<br />
			syms[i] = sym;<br />
		}<br />
	}</p>
<p>	return syms;<br />
}</p>
<p>/*<br />
 * collect histogram counts<br />
 */</p>
<p>static int<br />
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,<br />
		struct symbol *sym, u64 ip, struct ip_callchain *chain,<br />
		char level, u64 count)<br />
{<br />
	struct rb_node **p = &#038;hist.rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct hist_entry *he;<br />
	struct symbol **syms = NULL;<br />
	struct hist_entry entry = {<br />
		.thread	= thread,<br />
		.map	= map,<br />
		.dso	= dso,<br />
		.sym	= sym,<br />
		.ip	= ip,<br />
		.level	= level,<br />
		.count	= count,<br />
		.parent = NULL,<br />
		.sorted_chain = RB_ROOT<br />
	};<br />
	int cmp;</p>
<p>	if ((sort__has_parent || callchain) &#038;&#038; chain)<br />
		syms = resolve_callchain(thread, map, chain, &#038;entry);</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		he = rb_entry(parent, struct hist_entry, rb_node);</p>
<p>		cmp = hist_entry__cmp(&#038;entry, he);</p>
<p>		if (!cmp) {<br />
			he->count += count;<br />
			if (callchain) {<br />
				append_chain(&#038;he->callchain, chain, syms);<br />
				free(syms);<br />
			}<br />
			return 0;<br />
		}</p>
<p>		if (cmp < 0)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	he = malloc(sizeof(*he));<br />
	if (!he)<br />
		return -ENOMEM;<br />
	*he = entry;<br />
	if (callchain) {<br />
		callchain_init(&#038;he->callchain);<br />
		append_chain(&#038;he->callchain, chain, syms);<br />
		free(syms);<br />
	}<br />
	rb_link_node(&#038;he->rb_node, parent, p);<br />
	rb_insert_color(&#038;he->rb_node, &#038;hist);</p>
<p>	return 0;<br />
}</p>
<p>static void hist_entry__free(struct hist_entry *he)<br />
{<br />
	free(he);<br />
}</p>
<p>/*<br />
 * collapse the histogram<br />
 */</p>
<p>static struct rb_root collapse_hists;</p>
<p>static void collapse__insert_entry(struct hist_entry *he)<br />
{<br />
	struct rb_node **p = &#038;collapse_hists.rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct hist_entry *iter;<br />
	int64_t cmp;</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		iter = rb_entry(parent, struct hist_entry, rb_node);</p>
<p>		cmp = hist_entry__collapse(iter, he);</p>
<p>		if (!cmp) {<br />
			iter->count += he->count;<br />
			hist_entry__free(he);<br />
			return;<br />
		}</p>
<p>		if (cmp < 0)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;he->rb_node, parent, p);<br />
	rb_insert_color(&#038;he->rb_node, &#038;collapse_hists);<br />
}</p>
<p>static void collapse__resort(void)<br />
{<br />
	struct rb_node *next;<br />
	struct hist_entry *n;</p>
<p>	if (!sort__need_collapse)<br />
		return;</p>
<p>	next = rb_first(&#038;hist);<br />
	while (next) {<br />
		n = rb_entry(next, struct hist_entry, rb_node);<br />
		next = rb_next(&#038;n->rb_node);</p>
<p>		rb_erase(&#038;n->rb_node, &#038;hist);<br />
		collapse__insert_entry(n);<br />
	}<br />
}</p>
<p>/*<br />
 * reverse the map, sort on count.<br />
 */</p>
<p>static struct rb_root output_hists;</p>
<p>static void output__insert_entry(struct hist_entry *he, u64 min_callchain_hits)<br />
{<br />
	struct rb_node **p = &#038;output_hists.rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct hist_entry *iter;</p>
<p>	if (callchain)<br />
		callchain_param.sort(&#038;he->sorted_chain, &#038;he->callchain,<br />
				      min_callchain_hits, &#038;callchain_param);</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		iter = rb_entry(parent, struct hist_entry, rb_node);</p>
<p>		if (he->count > iter->count)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;he->rb_node, parent, p);<br />
	rb_insert_color(&#038;he->rb_node, &#038;output_hists);<br />
}</p>
<p>static void output__resort(u64 total_samples)<br />
{<br />
	struct rb_node *next;<br />
	struct hist_entry *n;<br />
	struct rb_root *tree = &hist;<br />
	u64 min_callchain_hits;</p>
<p>	min_callchain_hits = total_samples * (callchain_param.min_percent / 100);</p>
<p>	if (sort__need_collapse)<br />
		tree = &#038;collapse_hists;</p>
<p>	next = rb_first(tree);</p>
<p>	while (next) {<br />
		n = rb_entry(next, struct hist_entry, rb_node);<br />
		next = rb_next(&#038;n->rb_node);</p>
<p>		rb_erase(&#038;n->rb_node, tree);<br />
		output__insert_entry(n, min_callchain_hits);<br />
	}<br />
}</p>
<p>static size_t output__fprintf(FILE *fp, u64 total_samples)<br />
{<br />
	struct hist_entry *pos;<br />
	struct sort_entry *se;<br />
	struct rb_node *nd;<br />
	size_t ret = 0;<br />
	unsigned int width;<br />
	char *col_width = col_width_list_str;<br />
	int raw_printing_style;</p>
<p>	raw_printing_style = !strcmp(pretty_printing_style, &laquo;raw&raquo;);</p>
<p>	init_rem_hits();</p>
<p>	fprintf(fp, &laquo;# Samples: %Ld\n&raquo;, (u64)total_samples);<br />
	fprintf(fp, &laquo;#\n&raquo;);</p>
<p>	fprintf(fp, &laquo;# Overhead&raquo;);<br />
	if (show_nr_samples) {<br />
		if (field_sep)<br />
			fprintf(fp, &laquo;%cSamples&raquo;, *field_sep);<br />
		else<br />
			fputs(&raquo;  Samples  &laquo;, fp);<br />
	}<br />
	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		if (se->elide)<br />
			continue;<br />
		if (field_sep) {<br />
			fprintf(fp, &laquo;%c%s&raquo;, *field_sep, se->header);<br />
			continue;<br />
		}<br />
		width = strlen(se->header);<br />
		if (se->width) {<br />
			if (col_width_list_str) {<br />
				if (col_width) {<br />
					*se->width = atoi(col_width);<br />
					col_width = strchr(col_width, &#8216;,&#8217;);<br />
					if (col_width)<br />
						++col_width;<br />
				}<br />
			}<br />
			width = *se->width = max(*se->width, width);<br />
		}<br />
		fprintf(fp, &raquo;  %*s&raquo;, width, se->header);<br />
	}<br />
	fprintf(fp, &laquo;\n&raquo;);</p>
<p>	if (field_sep)<br />
		goto print_entries;</p>
<p>	fprintf(fp, &laquo;# &#8230;&#8230;..&raquo;);<br />
	if (show_nr_samples)<br />
		fprintf(fp, &raquo; &#8230;&#8230;&#8230;.&raquo;);<br />
	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		unsigned int i;</p>
<p>		if (se->elide)<br />
			continue;</p>
<p>		fprintf(fp, &raquo;  &laquo;);<br />
		if (se->width)<br />
			width = *se->width;<br />
		else<br />
			width = strlen(se->header);<br />
		for (i = 0; i < width; i++)<br />
			fprintf(fp, ".");<br />
	}<br />
	fprintf(fp, "\n");</p>
<p>	fprintf(fp, "#\n");</p>
<p>print_entries:<br />
	for (nd = rb_first(&#038;output_hists); nd; nd = rb_next(nd)) {<br />
		pos = rb_entry(nd, struct hist_entry, rb_node);<br />
		ret += hist_entry__fprintf(fp, pos, total_samples);<br />
	}</p>
<p>	if (sort_order == default_sort_order &#038;&#038;<br />
			parent_pattern == default_parent_pattern) {<br />
		fprintf(fp, "#\n");<br />
		fprintf(fp, "# (For a higher level overview, try: perf report --sort comm,dso)\n");<br />
		fprintf(fp, "#\n");<br />
	}<br />
	fprintf(fp, "\n");</p>
<p>	free(rem_sq_bracket);</p>
<p>	if (show_threads)<br />
		perf_read_values_display(fp, &#038;show_threads_values,<br />
					 raw_printing_style);</p>
<p>	return ret;<br />
}</p>
<p>static unsigned long total = 0,<br />
		     total_mmap = 0,<br />
		     total_comm = 0,<br />
		     total_fork = 0,<br />
		     total_unknown = 0,<br />
		     total_lost = 0;</p>
<p>static int validate_chain(struct ip_callchain *chain, event_t *event)<br />
{<br />
	unsigned int chain_size;</p>
<p>	chain_size = event->header.size;<br />
	chain_size -= (unsigned long)&#038;event->ip.__more_data &#8211; (unsigned long)event;</p>
<p>	if (chain->nr*sizeof(u64) > chain_size)<br />
		return -1;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_sample_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	char level;<br />
	int show = 0;<br />
	struct dso *dso = NULL;<br />
	struct thread *thread;<br />
	u64 ip = event->ip.ip;<br />
	u64 period = 1;<br />
	struct map *map = NULL;<br />
	void *more_data = event->ip.__more_data;<br />
	struct ip_callchain *chain = NULL;<br />
	int cpumode;</p>
<p>	thread = threads__findnew(event->ip.pid, &#038;threads, &#038;last_match);</p>
<p>	if (sample_type &#038; PERF_SAMPLE_PERIOD) {<br />
		period = *(u64 *)more_data;<br />
		more_data += sizeof(u64);<br />
	}</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->header.misc,<br />
		event->ip.pid, event->ip.tid,<br />
		(void *)(long)ip,<br />
		(long long)period);</p>
<p>	if (sample_type &#038; PERF_SAMPLE_CALLCHAIN) {<br />
		unsigned int i;</p>
<p>		chain = (void *)more_data;</p>
<p>		dump_printf(&raquo;&#8230; chain: nr:%Lu\n&raquo;, chain->nr);</p>
<p>		if (validate_chain(chain, event) < 0) {<br />
			eprintf("call-chain problem with event, skipping it.\n");<br />
			return 0;<br />
		}</p>
<p>		if (dump_trace) {<br />
			for (i = 0; i < chain->nr; i++)<br />
				dump_printf(&raquo;&#8230;.. %2d: %016Lx\n&raquo;, i, chain->ips[i]);<br />
		}<br />
	}</p>
<p>	dump_printf(&raquo; &#8230; thread: %s:%d\n&raquo;, thread->comm, thread->pid);</p>
<p>	if (thread == NULL) {<br />
		eprintf(&raquo;problem processing %d event, skipping it.\n&raquo;,<br />
			event->header.type);<br />
		return -1;<br />
	}</p>
<p>	if (comm_list &#038;&#038; !strlist__has_entry(comm_list, thread->comm))<br />
		return 0;</p>
<p>	cpumode = event->header.misc &#038; PERF_RECORD_MISC_CPUMODE_MASK;</p>
<p>	if (cpumode == PERF_RECORD_MISC_KERNEL) {<br />
		show = SHOW_KERNEL;<br />
		level = &#8216;k&#8217;;</p>
<p>		dso = kernel_dso;</p>
<p>		dump_printf(&raquo; &#8230;&#8230; dso: %s\n&raquo;, dso->name);</p>
<p>	} else if (cpumode == PERF_RECORD_MISC_USER) {</p>
<p>		show = SHOW_USER;<br />
		level = &#8216;.&#8217;;</p>
<p>	} else {<br />
		show = SHOW_HV;<br />
		level = &#8216;H&#8217;;</p>
<p>		dso = hypervisor_dso;</p>
<p>		dump_printf(&raquo; &#8230;&#8230; dso: [hypervisor]\n&raquo;);<br />
	}</p>
<p>	if (show &#038; show_mask) {<br />
		struct symbol *sym = resolve_symbol(thread, &#038;map, &#038;dso, &#038;ip);</p>
<p>		if (dso_list &#038;&#038; (!dso || !dso->name ||<br />
				 !strlist__has_entry(dso_list, dso->name)))<br />
			return 0;</p>
<p>		if (sym_list &#038;&#038; (!sym || !strlist__has_entry(sym_list, sym->name)))<br />
			return 0;</p>
<p>		if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {<br />
			eprintf(&raquo;problem incrementing symbol count, skipping event\n&raquo;);<br />
			return -1;<br />
		}<br />
	}<br />
	total += period;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_mmap_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;<br />
	struct map *map = map__new(&#038;event->mmap, cwd, cwdlen);</p>
<p>	thread = threads__findnew(event->mmap.pid, &#038;threads, &#038;last_match);</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_MMAP %d/%d: [%p(%p) @ %p]: %s\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->mmap.pid,<br />
		event->mmap.tid,<br />
		(void *)(long)event->mmap.start,<br />
		(void *)(long)event->mmap.len,<br />
		(void *)(long)event->mmap.pgoff,<br />
		event->mmap.filename);</p>
<p>	if (thread == NULL || map == NULL) {<br />
		dump_printf(&raquo;problem processing PERF_RECORD_MMAP, skipping event.\n&raquo;);<br />
		return 0;<br />
	}</p>
<p>	thread__insert_map(thread, map);<br />
	total_mmap++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_comm_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;</p>
<p>	thread = threads__findnew(event->comm.pid, &#038;threads, &#038;last_match);</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_COMM: %s:%d\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->comm.comm, event->comm.pid);</p>
<p>	if (thread == NULL ||<br />
	    thread__set_comm_adjust(thread, event->comm.comm)) {<br />
		dump_printf(&raquo;problem processing PERF_RECORD_COMM, skipping event.\n&raquo;);<br />
		return -1;<br />
	}<br />
	total_comm++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_task_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;<br />
	struct thread *parent;</p>
<p>	thread = threads__findnew(event->fork.pid, &#038;threads, &#038;last_match);<br />
	parent = threads__findnew(event->fork.ppid, &#038;threads, &#038;last_match);</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_%s: (%d:%d):(%d:%d)\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->header.type == PERF_RECORD_FORK ? &laquo;FORK&raquo; : &laquo;EXIT&raquo;,<br />
		event->fork.pid, event->fork.tid,<br />
		event->fork.ppid, event->fork.ptid);</p>
<p>	/*<br />
	 * A thread clone will have the same PID for both<br />
	 * parent and child.<br />
	 */<br />
	if (thread == parent)<br />
		return 0;</p>
<p>	if (event->header.type == PERF_RECORD_EXIT)<br />
		return 0;</p>
<p>	if (!thread || !parent || thread__fork(thread, parent)) {<br />
		dump_printf(&raquo;problem processing PERF_RECORD_FORK, skipping event.\n&raquo;);<br />
		return -1;<br />
	}<br />
	total_fork++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_lost_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	dump_printf(&raquo;%p [%p]: PERF_RECORD_LOST: id:%Ld: lost:%Ld\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->lost.id,<br />
		event->lost.lost);</p>
<p>	total_lost += event->lost.lost;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_read_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct perf_event_attr *attr;</p>
<p>	attr = perf_header__find_attr(event->read.id, header);</p>
<p>	if (show_threads) {<br />
		const char *name = attr ? __event_name(attr->type, attr->config)<br />
				   : &laquo;unknown&raquo;;<br />
		perf_read_values_add_value(&#038;show_threads_values,<br />
					   event->read.pid, event->read.tid,<br />
					   event->read.id,<br />
					   name,<br />
					   event->read.value);<br />
	}</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_READ: %d %d %s %Lu\n&raquo;,<br />
			(void *)(offset + head),<br />
			(void *)(long)(event->header.size),<br />
			event->read.pid,<br />
			event->read.tid,<br />
			attr ? __event_name(attr->type, attr->config)<br />
			     : &laquo;FAIL&raquo;,<br />
			event->read.value);</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	trace_event(event);</p>
<p>	switch (event->header.type) {<br />
	case PERF_RECORD_SAMPLE:<br />
		return process_sample_event(event, offset, head);</p>
<p>	case PERF_RECORD_MMAP:<br />
		return process_mmap_event(event, offset, head);</p>
<p>	case PERF_RECORD_COMM:<br />
		return process_comm_event(event, offset, head);</p>
<p>	case PERF_RECORD_FORK:<br />
	case PERF_RECORD_EXIT:<br />
		return process_task_event(event, offset, head);</p>
<p>	case PERF_RECORD_LOST:<br />
		return process_lost_event(event, offset, head);</p>
<p>	case PERF_RECORD_READ:<br />
		return process_read_event(event, offset, head);</p>
<p>	/*<br />
	 * We dont process them right now but they are fine:<br />
	 */</p>
<p>	case PERF_RECORD_THROTTLE:<br />
	case PERF_RECORD_UNTHROTTLE:<br />
		return 0;</p>
<p>	default:<br />
		return -1;<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static int __cmd_report(void)<br />
{<br />
	int ret, rc = EXIT_FAILURE;<br />
	unsigned long offset = 0;<br />
	unsigned long head, shift;<br />
	struct stat input_stat;<br />
	struct thread *idle;<br />
	event_t *event;<br />
	uint32_t size;<br />
	char *buf;</p>
<p>	idle = register_idle_thread(&#038;threads, &#038;last_match);<br />
	thread__comm_adjust(idle);</p>
<p>	if (show_threads)<br />
		perf_read_values_init(&#038;show_threads_values);</p>
<p>	input = open(input_name, O_RDONLY);<br />
	if (input < 0) {<br />
		fprintf(stderr, " failed to open file: %s", input_name);<br />
		if (!strcmp(input_name, "perf.data"))<br />
			fprintf(stderr, "  (try 'perf record' first)");<br />
		fprintf(stderr, "\n");<br />
		exit(-1);<br />
	}</p>
<p>	ret = fstat(input, &#038;input_stat);<br />
	if (ret < 0) {<br />
		perror("failed to stat file");<br />
		exit(-1);<br />
	}</p>
<p>	if (!force &#038;&#038; input_stat.st_uid &#038;&#038; (input_stat.st_uid != geteuid())) {<br />
		fprintf(stderr, "file: %s not owned by current user or root\n", input_name);<br />
		exit(-1);<br />
	}</p>
<p>	if (!input_stat.st_size) {<br />
		fprintf(stderr, "zero-sized file, nothing to do!\n");<br />
		exit(0);<br />
	}</p>
<p>	header = perf_header__read(input);<br />
	head = header->data_offset;</p>
<p>	sample_type = perf_header__sample_type(header);</p>
<p>	if (!(sample_type &#038; PERF_SAMPLE_CALLCHAIN)) {<br />
		if (sort__has_parent) {<br />
			fprintf(stderr, &laquo;selected &#8211;sort parent, but no&raquo;<br />
					&raquo; callchain data. Did you call&raquo;<br />
					&raquo; perf record without -g?\n&raquo;);<br />
			exit(-1);<br />
		}<br />
		if (callchain) {<br />
			fprintf(stderr, &laquo;selected -g but no callchain data.&raquo;<br />
					&raquo; Did you call perf record without&raquo;<br />
					&raquo; -g?\n&raquo;);<br />
			exit(-1);<br />
		}<br />
	} else if (callchain_param.mode != CHAIN_NONE &#038;&#038; !callchain) {<br />
			callchain = 1;<br />
			if (register_callchain_param(&#038;callchain_param) < 0) {<br />
				fprintf(stderr, "Can't register callchain"<br />
						" params\n");<br />
				exit(-1);<br />
			}<br />
	}</p>
<p>	if (load_kernel() < 0) {<br />
		perror("failed to load kernel symbols");<br />
		return EXIT_FAILURE;<br />
	}</p>
<p>	if (!full_paths) {<br />
		if (getcwd(__cwd, sizeof(__cwd)) == NULL) {<br />
			perror("failed to get the current directory");<br />
			return EXIT_FAILURE;<br />
		}<br />
		cwdlen = strlen(cwd);<br />
	} else {<br />
		cwd = NULL;<br />
		cwdlen = 0;<br />
	}</p>
<p>	shift = page_size * (head / page_size);<br />
	offset += shift;<br />
	head -= shift;</p>
<p>remap:<br />
	buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,<br />
			   MAP_SHARED, input, offset);<br />
	if (buf == MAP_FAILED) {<br />
		perror("failed to mmap file");<br />
		exit(-1);<br />
	}</p>
<p>more:<br />
	event = (event_t *)(buf + head);</p>
<p>	size = event->header.size;<br />
	if (!size)<br />
		size = 8;</p>
<p>	if (head + event->header.size >= page_size * mmap_window) {<br />
		int munmap_ret;</p>
<p>		shift = page_size * (head / page_size);</p>
<p>		munmap_ret = munmap(buf, page_size * mmap_window);<br />
		assert(munmap_ret == 0);</p>
<p>		offset += shift;<br />
		head -= shift;<br />
		goto remap;<br />
	}</p>
<p>	size = event->header.size;</p>
<p>	dump_printf(&raquo;\n%p [%p]: event: %d\n&raquo;,<br />
			(void *)(offset + head),<br />
			(void *)(long)event->header.size,<br />
			event->header.type);</p>
<p>	if (!size || process_event(event, offset, head) < 0) {</p>
<p>		dump_printf("%p [%p]: skipping unknown header type: %d\n",<br />
			(void *)(offset + head),<br />
			(void *)(long)(event->header.size),<br />
			event->header.type);</p>
<p>		total_unknown++;</p>
<p>		/*<br />
		 * assume we lost track of the stream, check alignment, and<br />
		 * increment a single u64 in the hope to catch on again &#8217;soon&#8217;.<br />
		 */</p>
<p>		if (unlikely(head &#038; 7))<br />
			head &#038;= ~7ULL;</p>
<p>		size = 8;<br />
	}</p>
<p>	head += size;</p>
<p>	if (offset + head >= header->data_offset + header->data_size)<br />
		goto done;</p>
<p>	if (offset + head < (unsigned long)input_stat.st_size)<br />
		goto more;</p>
<p>done:<br />
	rc = EXIT_SUCCESS;<br />
	close(input);</p>
<p>	dump_printf("      IP events: %10ld\n", total);<br />
	dump_printf("    mmap events: %10ld\n", total_mmap);<br />
	dump_printf("    comm events: %10ld\n", total_comm);<br />
	dump_printf("    fork events: %10ld\n", total_fork);<br />
	dump_printf("    lost events: %10ld\n", total_lost);<br />
	dump_printf(" unknown events: %10ld\n", total_unknown);</p>
<p>	if (dump_trace)<br />
		return 0;</p>
<p>	if (verbose >= 3)<br />
		threads__fprintf(stdout, &#038;threads);</p>
<p>	if (verbose >= 2)<br />
		dsos__fprintf(stdout);</p>
<p>	collapse__resort();<br />
	output__resort(total);<br />
	output__fprintf(stdout, total);</p>
<p>	if (show_threads)<br />
		perf_read_values_destroy(&#038;show_threads_values);</p>
<p>	return rc;<br />
}</p>
<p>static int<br />
parse_callchain_opt(const struct option *opt __used, const char *arg,<br />
		    int unset __used)<br />
{<br />
	char *tok;<br />
	char *endptr;</p>
<p>	callchain = 1;</p>
<p>	if (!arg)<br />
		return 0;</p>
<p>	tok = strtok((char *)arg, &laquo;,&raquo;);<br />
	if (!tok)<br />
		return -1;</p>
<p>	/* get the output mode */<br />
	if (!strncmp(tok, &laquo;graph&raquo;, strlen(arg)))<br />
		callchain_param.mode = CHAIN_GRAPH_ABS;</p>
<p>	else if (!strncmp(tok, &laquo;flat&raquo;, strlen(arg)))<br />
		callchain_param.mode = CHAIN_FLAT;</p>
<p>	else if (!strncmp(tok, &laquo;fractal&raquo;, strlen(arg)))<br />
		callchain_param.mode = CHAIN_GRAPH_REL;</p>
<p>	else if (!strncmp(tok, &laquo;none&raquo;, strlen(arg))) {<br />
		callchain_param.mode = CHAIN_NONE;<br />
		callchain = 0;</p>
<p>		return 0;<br />
	}</p>
<p>	else<br />
		return -1;</p>
<p>	/* get the min percentage */<br />
	tok = strtok(NULL, &laquo;,&raquo;);<br />
	if (!tok)<br />
		goto setup;</p>
<p>	callchain_param.min_percent = strtod(tok, &#038;endptr);<br />
	if (tok == endptr)<br />
		return -1;</p>
<p>setup:<br />
	if (register_callchain_param(&#038;callchain_param) < 0) {<br />
		fprintf(stderr, "Can't register callchain params\n");<br />
		return -1;<br />
	}<br />
	return 0;<br />
}</p>
<p>static const char * const report_usage[] = {<br />
	"perf report [<options>] <command>&laquo;,<br />
	NULL<br />
};</p>
<p>static const struct option options[] = {<br />
	OPT_STRING(&#8217;i', &laquo;input&raquo;, &#038;input_name, &laquo;file&raquo;,<br />
		    &laquo;input file name&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show symbol address, etc)&raquo;),<br />
	OPT_BOOLEAN(&#8217;D', &laquo;dump-raw-trace&raquo;, &#038;dump_trace,<br />
		    &laquo;dump raw trace in ASCII&raquo;),<br />
	OPT_STRING(&#8217;k', &laquo;vmlinux&raquo;, &#038;vmlinux_name, &laquo;file&raquo;, &laquo;vmlinux pathname&raquo;),<br />
	OPT_BOOLEAN(&#8217;f', &laquo;force&raquo;, &#038;force, &laquo;don&#8217;t complain, do it&raquo;),<br />
	OPT_BOOLEAN(&#8217;m', &laquo;modules&raquo;, &#038;modules,<br />
		    &laquo;load module symbols &#8211; WARNING: use only with -k and LIVE kernel&raquo;),<br />
	OPT_BOOLEAN(&#8217;n', &laquo;show-nr-samples&raquo;, &#038;show_nr_samples,<br />
		    &laquo;Show a column with the number of samples&raquo;),<br />
	OPT_BOOLEAN(&#8217;T', &laquo;threads&raquo;, &#038;show_threads,<br />
		    &laquo;Show per-thread event counters&raquo;),<br />
	OPT_STRING(0, &laquo;pretty&raquo;, &#038;pretty_printing_style, &laquo;key&raquo;,<br />
		   &laquo;pretty printing style key: normal raw&raquo;),<br />
	OPT_STRING(&#8217;s&#8217;, &laquo;sort&raquo;, &#038;sort_order, &laquo;key[,key2...]&laquo;,<br />
		   &laquo;sort by key(s): pid, comm, dso, symbol, parent&raquo;),<br />
	OPT_BOOLEAN(&#8217;P', &laquo;full-paths&raquo;, &#038;full_paths,<br />
		    &laquo;Don&#8217;t shorten the pathnames taking into account the cwd&raquo;),<br />
	OPT_STRING(&#8217;p', &laquo;parent&raquo;, &#038;parent_pattern, &laquo;regex&raquo;,<br />
		   &laquo;regex filter to identify parent, see: &#8216;&#8211;sort parent&#8217;&raquo;),<br />
	OPT_BOOLEAN(&#8217;x', &laquo;exclude-other&raquo;, &#038;exclude_other,<br />
		    &laquo;Only display entries with parent-match&raquo;),<br />
	OPT_CALLBACK_DEFAULT(&#8217;g', &laquo;call-graph&raquo;, NULL, &laquo;output_type,min_percent&raquo;,<br />
		     &laquo;Display callchains using output_type and min percent threshold. &raquo;<br />
		     &laquo;Default: fractal,0.5&#8243;, &#038;parse_callchain_opt, callchain_default_opt),<br />
	OPT_STRING(&#8217;d', &laquo;dsos&raquo;, &#038;dso_list_str, &laquo;dso[,dso...]&laquo;,<br />
		   &laquo;only consider symbols in these dsos&raquo;),<br />
	OPT_STRING(&#8217;C', &laquo;comms&raquo;, &#038;comm_list_str, &laquo;comm[,comm...]&laquo;,<br />
		   &laquo;only consider symbols in these comms&raquo;),<br />
	OPT_STRING(&#8217;S', &laquo;symbols&raquo;, &#038;sym_list_str, &laquo;symbol[,symbol...]&laquo;,<br />
		   &laquo;only consider these symbols&raquo;),<br />
	OPT_STRING(&#8217;w', &laquo;column-widths&raquo;, &#038;col_width_list_str,<br />
		   &laquo;width[,width...]&laquo;,<br />
		   &laquo;don&#8217;t try to adjust column width, use these fixed values&raquo;),<br />
	OPT_STRING(&#8217;t', &laquo;field-separator&raquo;, &#038;field_sep, &laquo;separator&raquo;,<br />
		   &laquo;separator for columns, no spaces will be added between &raquo;<br />
		   &laquo;columns &#8216;.&#8217; is reserved.&raquo;),<br />
	OPT_END()<br />
};</p>
<p>static void setup_sorting(void)<br />
{<br />
	char *tmp, *tok, *str = strdup(sort_order);</p>
<p>	for (tok = strtok_r(str, &laquo;, &laquo;, &#038;tmp);<br />
			tok; tok = strtok_r(NULL, &laquo;, &laquo;, &#038;tmp)) {<br />
		if (sort_dimension__add(tok) < 0) {<br />
			error("Unknown --sort key: `%s'", tok);<br />
			usage_with_options(report_usage, options);<br />
		}<br />
	}</p>
<p>	free(str);<br />
}</p>
<p>static void setup_list(struct strlist **list, const char *list_str,<br />
		       struct sort_entry *se, const char *list_name,<br />
		       FILE *fp)<br />
{<br />
	if (list_str) {<br />
		*list = strlist__new(true, list_str);<br />
		if (!*list) {<br />
			fprintf(stderr, "problems parsing %s list\n",<br />
				list_name);<br />
			exit(129);<br />
		}<br />
		if (strlist__nr_entries(*list) == 1) {<br />
			fprintf(fp, "# %s: %s\n", list_name,<br />
				strlist__entry(*list, 0)->s);<br />
			se->elide = true;<br />
		}<br />
	}<br />
}</p>
<p>int cmd_report(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	symbol__init();</p>
<p>	page_size = getpagesize();</p>
<p>	argc = parse_options(argc, argv, options, report_usage, 0);</p>
<p>	setup_sorting();</p>
<p>	if (parent_pattern != default_parent_pattern) {<br />
		sort_dimension__add(&raquo;parent&raquo;);<br />
		sort_parent.elide = 1;<br />
	} else<br />
		exclude_other = 0;</p>
<p>	/*<br />
	 * Any (unrecognized) arguments left?<br />
	 */<br />
	if (argc)<br />
		usage_with_options(report_usage, options);</p>
<p>	setup_pager();</p>
<p>	setup_list(&#038;dso_list, dso_list_str, &#038;sort_dso, &laquo;dso&raquo;, stdout);<br />
	setup_list(&#038;comm_list, comm_list_str, &#038;sort_comm, &laquo;comm&raquo;, stdout);<br />
	setup_list(&#038;sym_list, sym_list_str, &#038;sort_sym, &laquo;symbol&raquo;, stdout);</p>
<p>	if (field_sep &#038;&#038; *field_sep == &#8216;.&#8217;) {<br />
		fputs(&raquo;&#8216;.&#8217; is the only non valid &#8211;field-separator argument\n&raquo;,<br />
		      stderr);<br />
		exit(129);<br />
	}</p>
<p>	return __cmd_report();<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-report-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-record.c</title>
		<link>http://lynyrd.ru/builtin-record-c</link>
		<comments>http://lynyrd.ru/builtin-record-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:26:33 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=601</guid>
		<description><![CDATA[/*
 * builtin-record.c
 *
 * Builtin record command: Record the profile of a workload
 * (or a CPU, or a PID) into the perf.data output file &#8211; for
 * later analysis via perf report.
 */
#include &#171;builtin.h&#187;
#include &#171;perf.h&#187;
#include &#171;util/util.h&#187;
#include &#171;util/parse-options.h&#187;
#include &#171;util/parse-events.h&#187;
#include &#171;util/string.h&#187;
#include &#171;util/header.h&#187;
#include &#171;util/event.h&#187;
#include &#171;util/debug.h&#187;
#include &#171;util/trace-event.h&#187;
#include 
#include 
#define ALIGN(x, a)		__ALIGN_MASK(x, (typeof(x))(a)-1)
#define __ALIGN_MASK(x, mask)	(((x)+(mask))&#038;~(mask))
static int			fd[MAX_NR_CPUS][MAX_COUNTERS];
static long			default_interval		= 100000;
static ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-record.c<span id="more-601"></span><br />
 *<br />
 * Builtin record command: Record the profile of a workload<br />
 * (or a CPU, or a PID) into the perf.data output file &#8211; for<br />
 * later analysis via perf report.<br />
 */<br />
#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;perf.h&raquo;</p>
<p>#include &laquo;util/util.h&raquo;<br />
#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;<br />
#include &laquo;util/string.h&raquo;</p>
<p>#include &laquo;util/header.h&raquo;<br />
#include &laquo;util/event.h&raquo;<br />
#include &laquo;util/debug.h&raquo;<br />
#include &laquo;util/trace-event.h&raquo;</p>
<p>#include <unistd.h><br />
#include <sched.h></p>
<p>#define ALIGN(x, a)		__ALIGN_MASK(x, (typeof(x))(a)-1)<br />
#define __ALIGN_MASK(x, mask)	(((x)+(mask))&#038;~(mask))</p>
<p>static int			fd[MAX_NR_CPUS][MAX_COUNTERS];</p>
<p>static long			default_interval		= 100000;</p>
<p>static int			nr_cpus				= 0;<br />
static unsigned int		page_size;<br />
static unsigned int		mmap_pages			= 128;<br />
static int			freq				= 0;<br />
static int			output;<br />
static const char		*output_name			= &laquo;perf.data&raquo;;<br />
static int			group				= 0;<br />
static unsigned int		realtime_prio			= 0;<br />
static int			raw_samples			= 0;<br />
static int			system_wide			= 0;<br />
static int			profile_cpu			= -1;<br />
static pid_t			target_pid			= -1;<br />
static pid_t			child_pid			= -1;<br />
static int			inherit				= 1;<br />
static int			force				= 0;<br />
static int			append_file			= 0;<br />
static int			call_graph			= 0;<br />
static int			inherit_stat			= 0;<br />
static int			no_samples			= 0;<br />
static int			sample_address			= 0;<br />
static int			multiplex			= 0;<br />
static int			multiplex_fd			= -1;</p>
<p>static long			samples;<br />
static struct timeval		last_read;<br />
static struct timeval		this_read;</p>
<p>static u64			bytes_written;</p>
<p>static struct pollfd		event_array[MAX_NR_CPUS * MAX_COUNTERS];</p>
<p>static int			nr_poll;<br />
static int			nr_cpu;</p>
<p>static int			file_new = 1;</p>
<p>struct perf_header		*header;</p>
<p>struct mmap_data {<br />
	int			counter;<br />
	void			*base;<br />
	unsigned int		mask;<br />
	unsigned int		prev;<br />
};</p>
<p>static struct mmap_data		mmap_array[MAX_NR_CPUS][MAX_COUNTERS];</p>
<p>static unsigned long mmap_read_head(struct mmap_data *md)<br />
{<br />
	struct perf_event_mmap_page *pc = md->base;<br />
	long head;</p>
<p>	head = pc->data_head;<br />
	rmb();</p>
<p>	return head;<br />
}</p>
<p>static void mmap_write_tail(struct mmap_data *md, unsigned long tail)<br />
{<br />
	struct perf_event_mmap_page *pc = md->base;</p>
<p>	/*<br />
	 * ensure all reads are done before we write the tail out.<br />
	 */<br />
	/* mb(); */<br />
	pc->data_tail = tail;<br />
}</p>
<p>static void write_output(void *buf, size_t size)<br />
{<br />
	while (size) {<br />
		int ret = write(output, buf, size);</p>
<p>		if (ret < 0)<br />
			die("failed to write");</p>
<p>		size -= ret;<br />
		buf += ret;</p>
<p>		bytes_written += ret;<br />
	}<br />
}</p>
<p>static void mmap_read(struct mmap_data *md)<br />
{<br />
	unsigned int head = mmap_read_head(md);<br />
	unsigned int old = md->prev;<br />
	unsigned char *data = md->base + page_size;<br />
	unsigned long size;<br />
	void *buf;<br />
	int diff;</p>
<p>	gettimeofday(&#038;this_read, NULL);</p>
<p>	/*<br />
	 * If we&#8217;re further behind than half the buffer, there&#8217;s a chance<br />
	 * the writer will bite our tail and mess up the samples under us.<br />
	 *<br />
	 * If we somehow ended up ahead of the head, we got messed up.<br />
	 *<br />
	 * In either case, truncate and restart at head.<br />
	 */<br />
	diff = head &#8211; old;<br />
	if (diff < 0) {<br />
		struct timeval iv;<br />
		unsigned long msecs;</p>
<p>		timersub(&#038;this_read, &#038;last_read, &#038;iv);<br />
		msecs = iv.tv_sec*1000 + iv.tv_usec/1000;</p>
<p>		fprintf(stderr, "WARNING: failed to keep up with mmap data."<br />
				"  Last read %lu msecs ago.\n", msecs);</p>
<p>		/*<br />
		 * head points to a known good entry, start there.<br />
		 */<br />
		old = head;<br />
	}</p>
<p>	last_read = this_read;</p>
<p>	if (old != head)<br />
		samples++;</p>
<p>	size = head - old;</p>
<p>	if ((old &#038; md->mask) + size != (head &#038; md->mask)) {<br />
		buf = &#038;data[old &#038; md->mask];<br />
		size = md->mask + 1 &#8211; (old &#038; md->mask);<br />
		old += size;</p>
<p>		write_output(buf, size);<br />
	}</p>
<p>	buf = &#038;data[old &#038; md->mask];<br />
	size = head &#8211; old;<br />
	old += size;</p>
<p>	write_output(buf, size);</p>
<p>	md->prev = old;<br />
	mmap_write_tail(md, old);<br />
}</p>
<p>static volatile int done = 0;<br />
static volatile int signr = -1;</p>
<p>static void sig_handler(int sig)<br />
{<br />
	done = 1;<br />
	signr = sig;<br />
}</p>
<p>static void sig_atexit(void)<br />
{<br />
	if (child_pid != -1)<br />
		kill(child_pid, SIGTERM);</p>
<p>	if (signr == -1)<br />
		return;</p>
<p>	signal(signr, SIG_DFL);<br />
	kill(getpid(), signr);<br />
}</p>
<p>static pid_t pid_synthesize_comm_event(pid_t pid, int full)<br />
{<br />
	struct comm_event comm_ev;<br />
	char filename[PATH_MAX];<br />
	char bf[BUFSIZ];<br />
	FILE *fp;<br />
	size_t size = 0;<br />
	DIR *tasks;<br />
	struct dirent dirent, *next;<br />
	pid_t tgid = 0;</p>
<p>	snprintf(filename, sizeof(filename), &laquo;/proc/%d/status&raquo;, pid);</p>
<p>	fp = fopen(filename, &laquo;r&raquo;);<br />
	if (fp == NULL) {<br />
		/*<br />
		 * We raced with a task exiting &#8211; just return:<br />
		 */<br />
		if (verbose)<br />
			fprintf(stderr, &laquo;couldn&#8217;t open %s\n&raquo;, filename);<br />
		return 0;<br />
	}</p>
<p>	memset(&#038;comm_ev, 0, sizeof(comm_ev));<br />
	while (!comm_ev.comm[0] || !comm_ev.pid) {<br />
		if (fgets(bf, sizeof(bf), fp) == NULL)<br />
			goto out_failure;</p>
<p>		if (memcmp(bf, &laquo;Name:&raquo;, 5) == 0) {<br />
			char *name = bf + 5;<br />
			while (*name &#038;&#038; isspace(*name))<br />
				++name;<br />
			size = strlen(name) &#8211; 1;<br />
			memcpy(comm_ev.comm, name, size++);<br />
		} else if (memcmp(bf, &laquo;Tgid:&raquo;, 5) == 0) {<br />
			char *tgids = bf + 5;<br />
			while (*tgids &#038;&#038; isspace(*tgids))<br />
				++tgids;<br />
			tgid = comm_ev.pid = atoi(tgids);<br />
		}<br />
	}</p>
<p>	comm_ev.header.type = PERF_RECORD_COMM;<br />
	size = ALIGN(size, sizeof(u64));<br />
	comm_ev.header.size = sizeof(comm_ev) &#8211; (sizeof(comm_ev.comm) &#8211; size);</p>
<p>	if (!full) {<br />
		comm_ev.tid = pid;</p>
<p>		write_output(&#038;comm_ev, comm_ev.header.size);<br />
		goto out_fclose;<br />
	}</p>
<p>	snprintf(filename, sizeof(filename), &laquo;/proc/%d/task&raquo;, pid);</p>
<p>	tasks = opendir(filename);<br />
	while (!readdir_r(tasks, &#038;dirent, &#038;next) &#038;&#038; next) {<br />
		char *end;<br />
		pid = strtol(dirent.d_name, &#038;end, 10);<br />
		if (*end)<br />
			continue;</p>
<p>		comm_ev.tid = pid;</p>
<p>		write_output(&#038;comm_ev, comm_ev.header.size);<br />
	}<br />
	closedir(tasks);</p>
<p>out_fclose:<br />
	fclose(fp);<br />
	return tgid;</p>
<p>out_failure:<br />
	fprintf(stderr, &laquo;couldn&#8217;t get COMM and pgid, malformed %s\n&raquo;,<br />
		filename);<br />
	exit(EXIT_FAILURE);<br />
}</p>
<p>static void pid_synthesize_mmap_samples(pid_t pid, pid_t tgid)<br />
{<br />
	char filename[PATH_MAX];<br />
	FILE *fp;</p>
<p>	snprintf(filename, sizeof(filename), &laquo;/proc/%d/maps&raquo;, pid);</p>
<p>	fp = fopen(filename, &laquo;r&raquo;);<br />
	if (fp == NULL) {<br />
		/*<br />
		 * We raced with a task exiting &#8211; just return:<br />
		 */<br />
		if (verbose)<br />
			fprintf(stderr, &laquo;couldn&#8217;t open %s\n&raquo;, filename);<br />
		return;<br />
	}<br />
	while (1) {<br />
		char bf[BUFSIZ], *pbf = bf;<br />
		struct mmap_event mmap_ev = {<br />
			.header = { .type = PERF_RECORD_MMAP },<br />
		};<br />
		int n;<br />
		size_t size;<br />
		if (fgets(bf, sizeof(bf), fp) == NULL)<br />
			break;</p>
<p>		/* 00400000-0040c000 r-xp 00000000 fd:01 41038  /bin/cat */<br />
		n = hex2u64(pbf, &#038;mmap_ev.start);<br />
		if (n < 0)<br />
			continue;<br />
		pbf += n + 1;<br />
		n = hex2u64(pbf, &#038;mmap_ev.len);<br />
		if (n < 0)<br />
			continue;<br />
		pbf += n + 3;<br />
		if (*pbf == 'x') { /* vm_exec */<br />
			char *execname = strchr(bf, '/');</p>
<p>			/* Catch VDSO */<br />
			if (execname == NULL)<br />
				execname = strstr(bf, "[vdso]");</p>
<p>			if (execname == NULL)<br />
				continue;</p>
<p>			size = strlen(execname);<br />
			execname[size - 1] = '\0'; /* Remove \n */<br />
			memcpy(mmap_ev.filename, execname, size);<br />
			size = ALIGN(size, sizeof(u64));<br />
			mmap_ev.len -= mmap_ev.start;<br />
			mmap_ev.header.size = (sizeof(mmap_ev) -<br />
					       (sizeof(mmap_ev.filename) - size));<br />
			mmap_ev.pid = tgid;<br />
			mmap_ev.tid = pid;</p>
<p>			write_output(&#038;mmap_ev, mmap_ev.header.size);<br />
		}<br />
	}</p>
<p>	fclose(fp);<br />
}</p>
<p>static void synthesize_all(void)<br />
{<br />
	DIR *proc;<br />
	struct dirent dirent, *next;</p>
<p>	proc = opendir("/proc");</p>
<p>	while (!readdir_r(proc, &#038;dirent, &#038;next) &#038;&#038; next) {<br />
		char *end;<br />
		pid_t pid, tgid;</p>
<p>		pid = strtol(dirent.d_name, &#038;end, 10);<br />
		if (*end) /* only interested in proper numerical dirents */<br />
			continue;</p>
<p>		tgid = pid_synthesize_comm_event(pid, 1);<br />
		pid_synthesize_mmap_samples(pid, tgid);<br />
	}</p>
<p>	closedir(proc);<br />
}</p>
<p>static int group_fd;</p>
<p>static struct perf_header_attr *get_header_attr(struct perf_event_attr *a, int nr)<br />
{<br />
	struct perf_header_attr *h_attr;</p>
<p>	if (nr < header->attrs) {<br />
		h_attr = header->attr[nr];<br />
	} else {<br />
		h_attr = perf_header_attr__new(a);<br />
		perf_header__add_attr(header, h_attr);<br />
	}</p>
<p>	return h_attr;<br />
}</p>
<p>static void create_counter(int counter, int cpu, pid_t pid)<br />
{<br />
	struct perf_event_attr *attr = attrs + counter;<br />
	struct perf_header_attr *h_attr;<br />
	int track = !counter; /* only the first counter needs these */<br />
	struct {<br />
		u64 count;<br />
		u64 time_enabled;<br />
		u64 time_running;<br />
		u64 id;<br />
	} read_data;</p>
<p>	attr->read_format	= PERF_FORMAT_TOTAL_TIME_ENABLED |<br />
				  PERF_FORMAT_TOTAL_TIME_RUNNING |<br />
				  PERF_FORMAT_ID;</p>
<p>	attr->sample_type	|= PERF_SAMPLE_IP | PERF_SAMPLE_TID;</p>
<p>	if (freq) {<br />
		attr->sample_type	|= PERF_SAMPLE_PERIOD;<br />
		attr->freq		= 1;<br />
		attr->sample_freq	= freq;<br />
	}</p>
<p>	if (no_samples)<br />
		attr->sample_freq = 0;</p>
<p>	if (inherit_stat)<br />
		attr->inherit_stat = 1;</p>
<p>	if (sample_address)<br />
		attr->sample_type	|= PERF_SAMPLE_ADDR;</p>
<p>	if (call_graph)<br />
		attr->sample_type	|= PERF_SAMPLE_CALLCHAIN;</p>
<p>	if (raw_samples) {<br />
		attr->sample_type	|= PERF_SAMPLE_TIME;<br />
		attr->sample_type	|= PERF_SAMPLE_RAW;<br />
		attr->sample_type	|= PERF_SAMPLE_CPU;<br />
	}</p>
<p>	attr->mmap		= track;<br />
	attr->comm		= track;<br />
	attr->inherit		= (cpu < 0) &#038;& inherit;<br />
	attr->disabled		= 1;</p>
<p>try_again:<br />
	fd[nr_cpu][counter] = sys_perf_event_open(attr, pid, cpu, group_fd, 0);</p>
<p>	if (fd[nr_cpu][counter] < 0) {<br />
		int err = errno;</p>
<p>		if (err == EPERM || err == EACCES)<br />
			die("Permission error - are you root?\n");<br />
		else if (err ==  ENODEV &#038;&#038; profile_cpu != -1)<br />
			die("No such device - did you specify an out-of-range profile CPU?\n");</p>
<p>		/*<br />
		 * If it's cycles then fall back to hrtimer<br />
		 * based cpu-clock-tick sw counter, which<br />
		 * is always available even if no PMU support:<br />
		 */<br />
		if (attr->type == PERF_TYPE_HARDWARE<br />
			&#038;&#038; attr->config == PERF_COUNT_HW_CPU_CYCLES) {</p>
<p>			if (verbose)<br />
				warning(&raquo; &#8230; trying to fall back to cpu-clock-ticks\n&raquo;);<br />
			attr->type = PERF_TYPE_SOFTWARE;<br />
			attr->config = PERF_COUNT_SW_CPU_CLOCK;<br />
			goto try_again;<br />
		}<br />
		printf(&raquo;\n&raquo;);<br />
		error(&raquo;perfcounter syscall returned with %d (%s)\n&raquo;,<br />
			fd[nr_cpu][counter], strerror(err));<br />
		die(&raquo;No CONFIG_PERF_EVENTS=y kernel support configured?\n&raquo;);<br />
		exit(-1);<br />
	}</p>
<p>	h_attr = get_header_attr(attr, counter);</p>
<p>	if (!file_new) {<br />
		if (memcmp(&#038;h_attr->attr, attr, sizeof(*attr))) {<br />
			fprintf(stderr, &laquo;incompatible append\n&raquo;);<br />
			exit(-1);<br />
		}<br />
	}</p>
<p>	if (read(fd[nr_cpu][counter], &#038;read_data, sizeof(read_data)) == -1) {<br />
		perror(&raquo;Unable to read perf file descriptor\n&raquo;);<br />
		exit(-1);<br />
	}</p>
<p>	perf_header_attr__add_id(h_attr, read_data.id);</p>
<p>	assert(fd[nr_cpu][counter] >= 0);<br />
	fcntl(fd[nr_cpu][counter], F_SETFL, O_NONBLOCK);</p>
<p>	/*<br />
	 * First counter acts as the group leader:<br />
	 */<br />
	if (group &#038;&#038; group_fd == -1)<br />
		group_fd = fd[nr_cpu][counter];<br />
	if (multiplex &#038;&#038; multiplex_fd == -1)<br />
		multiplex_fd = fd[nr_cpu][counter];</p>
<p>	if (multiplex &#038;&#038; fd[nr_cpu][counter] != multiplex_fd) {<br />
		int ret;</p>
<p>		ret = ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_SET_OUTPUT, multiplex_fd);<br />
		assert(ret != -1);<br />
	} else {<br />
		event_array[nr_poll].fd = fd[nr_cpu][counter];<br />
		event_array[nr_poll].events = POLLIN;<br />
		nr_poll++;</p>
<p>		mmap_array[nr_cpu][counter].counter = counter;<br />
		mmap_array[nr_cpu][counter].prev = 0;<br />
		mmap_array[nr_cpu][counter].mask = mmap_pages*page_size &#8211; 1;<br />
		mmap_array[nr_cpu][counter].base = mmap(NULL, (mmap_pages+1)*page_size,<br />
				PROT_READ|PROT_WRITE, MAP_SHARED, fd[nr_cpu][counter], 0);<br />
		if (mmap_array[nr_cpu][counter].base == MAP_FAILED) {<br />
			error(&raquo;failed to mmap with %d (%s)\n&raquo;, errno, strerror(errno));<br />
			exit(-1);<br />
		}<br />
	}</p>
<p>	ioctl(fd[nr_cpu][counter], PERF_EVENT_IOC_ENABLE);<br />
}</p>
<p>static void open_counters(int cpu, pid_t pid)<br />
{<br />
	int counter;</p>
<p>	group_fd = -1;<br />
	for (counter = 0; counter < nr_counters; counter++)<br />
		create_counter(counter, cpu, pid);</p>
<p>	nr_cpu++;<br />
}</p>
<p>static void atexit_header(void)<br />
{<br />
	header->data_size += bytes_written;</p>
<p>	perf_header__write(header, output);<br />
}</p>
<p>static int __cmd_record(int argc, const char **argv)<br />
{<br />
	int i, counter;<br />
	struct stat st;<br />
	pid_t pid = 0;<br />
	int flags;<br />
	int ret;<br />
	unsigned long waking = 0;</p>
<p>	page_size = sysconf(_SC_PAGE_SIZE);<br />
	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);<br />
	assert(nr_cpus <= MAX_NR_CPUS);<br />
	assert(nr_cpus >= 0);</p>
<p>	atexit(sig_atexit);<br />
	signal(SIGCHLD, sig_handler);<br />
	signal(SIGINT, sig_handler);</p>
<p>	if (!stat(output_name, &#038;st) &#038;&#038; st.st_size) {<br />
		if (!force &#038;&#038; !append_file) {<br />
			fprintf(stderr, &laquo;Error, output file %s exists, use -A to append or -f to overwrite.\n&raquo;,<br />
					output_name);<br />
			exit(-1);<br />
		}<br />
	} else {<br />
		append_file = 0;<br />
	}</p>
<p>	flags = O_CREAT|O_RDWR;<br />
	if (append_file)<br />
		file_new = 0;<br />
	else<br />
		flags |= O_TRUNC;</p>
<p>	output = open(output_name, flags, S_IRUSR|S_IWUSR);<br />
	if (output < 0) {<br />
		perror("failed to create output file");<br />
		exit(-1);<br />
	}</p>
<p>	if (!file_new)<br />
		header = perf_header__read(output);<br />
	else<br />
		header = perf_header__new();</p>
<p>	if (raw_samples) {<br />
		read_tracing_data(attrs, nr_counters);<br />
	} else {<br />
		for (i = 0; i < nr_counters; i++) {<br />
			if (attrs[i].sample_type &#038; PERF_SAMPLE_RAW) {<br />
				read_tracing_data(attrs, nr_counters);<br />
				break;<br />
			}<br />
		}<br />
	}<br />
	atexit(atexit_header);</p>
<p>	if (!system_wide) {<br />
		pid = target_pid;<br />
		if (pid == -1)<br />
			pid = getpid();</p>
<p>		open_counters(profile_cpu, pid);<br />
	} else {<br />
		if (profile_cpu != -1) {<br />
			open_counters(profile_cpu, target_pid);<br />
		} else {<br />
			for (i = 0; i < nr_cpus; i++)<br />
				open_counters(i, target_pid);<br />
		}<br />
	}</p>
<p>	if (file_new)<br />
		perf_header__write(header, output);</p>
<p>	if (!system_wide) {<br />
		pid_t tgid = pid_synthesize_comm_event(pid, 0);<br />
		pid_synthesize_mmap_samples(pid, tgid);<br />
	} else<br />
		synthesize_all();</p>
<p>	if (target_pid == -1 &#038;&#038; argc) {<br />
		pid = fork();<br />
		if (pid < 0)<br />
			perror("failed to fork");</p>
<p>		if (!pid) {<br />
			if (execvp(argv[0], (char **)argv)) {<br />
				perror(argv[0]);<br />
				exit(-1);<br />
			}<br />
		}</p>
<p>		child_pid = pid;<br />
	}</p>
<p>	if (realtime_prio) {<br />
		struct sched_param param;</p>
<p>		param.sched_priority = realtime_prio;<br />
		if (sched_setscheduler(0, SCHED_FIFO, &#038;param)) {<br />
			printf("Could not set realtime priority.\n");<br />
			exit(-1);<br />
		}<br />
	}</p>
<p>	for (;;) {<br />
		int hits = samples;</p>
<p>		for (i = 0; i < nr_cpu; i++) {<br />
			for (counter = 0; counter < nr_counters; counter++) {<br />
				if (mmap_array[i][counter].base)<br />
					mmap_read(&#038;mmap_array[i][counter]);<br />
			}<br />
		}</p>
<p>		if (hits == samples) {<br />
			if (done)<br />
				break;<br />
			ret = poll(event_array, nr_poll, -1);<br />
			waking++;<br />
		}</p>
<p>		if (done) {<br />
			for (i = 0; i < nr_cpu; i++) {<br />
				for (counter = 0; counter < nr_counters; counter++)<br />
					ioctl(fd[i][counter], PERF_EVENT_IOC_DISABLE);<br />
			}<br />
		}<br />
	}</p>
<p>	fprintf(stderr, "[ perf record: Woken up %ld times to write data ]\n", waking);</p>
<p>	/*<br />
	 * Approximate RIP event size: 24 bytes.<br />
	 */<br />
	fprintf(stderr,<br />
		"[ perf record: Captured and wrote %.3f MB %s (~%lld samples) ]\n",<br />
		(double)bytes_written / 1024.0 / 1024.0,<br />
		output_name,<br />
		bytes_written / 24);</p>
<p>	return 0;<br />
}</p>
<p>static const char * const record_usage[] = {<br />
	"perf record [<options>] [<command>]&laquo;,<br />
	&laquo;perf record [<options>] &#8212; <command> [<options>]&laquo;,<br />
	NULL<br />
};</p>
<p>static const struct option options[] = {<br />
	OPT_CALLBACK(&#8217;e', &laquo;event&raquo;, NULL, &laquo;event&raquo;,<br />
		     &laquo;event selector. use &#8216;perf list&#8217; to list available events&raquo;,<br />
		     parse_events),<br />
	OPT_INTEGER(&#8217;p', &laquo;pid&raquo;, &#038;target_pid,<br />
		    &laquo;record events on existing pid&raquo;),<br />
	OPT_INTEGER(&#8217;r', &laquo;realtime&raquo;, &#038;realtime_prio,<br />
		    &laquo;collect data with this RT SCHED_FIFO priority&raquo;),<br />
	OPT_BOOLEAN(&#8217;R', &laquo;raw-samples&raquo;, &#038;raw_samples,<br />
		    &laquo;collect raw sample records from all opened counters&raquo;),<br />
	OPT_BOOLEAN(&#8217;a', &laquo;all-cpus&raquo;, &#038;system_wide,<br />
			    &laquo;system-wide collection from all CPUs&raquo;),<br />
	OPT_BOOLEAN(&#8217;A', &laquo;append&raquo;, &#038;append_file,<br />
			    &laquo;append to the output file to do incremental profiling&raquo;),<br />
	OPT_INTEGER(&#8217;C', &laquo;profile_cpu&raquo;, &#038;profile_cpu,<br />
			    &laquo;CPU to profile on&raquo;),<br />
	OPT_BOOLEAN(&#8217;f', &laquo;force&raquo;, &#038;force,<br />
			&laquo;overwrite existing data file&raquo;),<br />
	OPT_LONG(&#8217;c', &laquo;count&raquo;, &#038;default_interval,<br />
		    &laquo;event period to sample&raquo;),<br />
	OPT_STRING(&#8217;o', &laquo;output&raquo;, &#038;output_name, &laquo;file&raquo;,<br />
		    &laquo;output file name&raquo;),<br />
	OPT_BOOLEAN(&#8217;i', &laquo;inherit&raquo;, &#038;inherit,<br />
		    &laquo;child tasks inherit counters&raquo;),<br />
	OPT_INTEGER(&#8217;F', &laquo;freq&raquo;, &#038;freq,<br />
		    &laquo;profile at this frequency&raquo;),<br />
	OPT_INTEGER(&#8217;m', &laquo;mmap-pages&raquo;, &#038;mmap_pages,<br />
		    &laquo;number of mmap data pages&raquo;),<br />
	OPT_BOOLEAN(&#8217;g', &laquo;call-graph&raquo;, &#038;call_graph,<br />
		    &laquo;do call-graph (stack chain/backtrace) recording&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show counter open errors, etc)&raquo;),<br />
	OPT_BOOLEAN(&#8217;s&#8217;, &laquo;stat&raquo;, &#038;inherit_stat,<br />
		    &laquo;per thread counts&raquo;),<br />
	OPT_BOOLEAN(&#8217;d', &laquo;data&raquo;, &#038;sample_address,<br />
		    &laquo;Sample addresses&raquo;),<br />
	OPT_BOOLEAN(&#8217;n', &laquo;no-samples&raquo;, &#038;no_samples,<br />
		    &laquo;don&#8217;t sample&raquo;),<br />
	OPT_BOOLEAN(&#8217;M', &laquo;multiplex&raquo;, &#038;multiplex,<br />
		    &laquo;multiplex counter output in a single channel&raquo;),<br />
	OPT_END()<br />
};</p>
<p>int cmd_record(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	int counter;</p>
<p>	argc = parse_options(argc, argv, options, record_usage,<br />
		PARSE_OPT_STOP_AT_NON_OPTION);<br />
	if (!argc &#038;&#038; target_pid == -1 &#038;&#038; !system_wide)<br />
		usage_with_options(record_usage, options);</p>
<p>	if (!nr_counters) {<br />
		nr_counters	= 1;<br />
		attrs[0].type	= PERF_TYPE_HARDWARE;<br />
		attrs[0].config = PERF_COUNT_HW_CPU_CYCLES;<br />
	}</p>
<p>	for (counter = 0; counter < nr_counters; counter++) {<br />
		if (attrs[counter].sample_period)<br />
			continue;</p>
<p>		attrs[counter].sample_period = default_interval;<br />
	}</p>
<p>	return __cmd_record(argc, argv);<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-record-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-list.c</title>
		<link>http://lynyrd.ru/builtin-list-c</link>
		<comments>http://lynyrd.ru/builtin-list-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:26:05 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/builtin-list-c</guid>
		<description><![CDATA[/*
 * builtin-list.c
 *
 * Builtin list command: list all event types
 *
 * Copyright (C) 2009, Thomas Gleixner 
 * Copyright (C) 2008-2009, Red Hat Inc, Ingo Molnar 
 */
#include &#171;builtin.h&#187;
#include &#171;perf.h&#187;
#include &#171;util/parse-events.h&#187;
#include &#171;util/cache.h&#187;
int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)
{
	setup_pager();
	print_events();
	return 0;
}
]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-list.c<span id="more-600"></span><br />
 *<br />
 * Builtin list command: list all event types<br />
 *<br />
 * Copyright (C) 2009, Thomas Gleixner <tglx@linutronix.de><br />
 * Copyright (C) 2008-2009, Red Hat Inc, Ingo Molnar <mingo@redhat.com><br />
 */<br />
#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;perf.h&raquo;</p>
<p>#include &laquo;util/parse-events.h&raquo;<br />
#include &laquo;util/cache.h&raquo;</p>
<p>int cmd_list(int argc __used, const char **argv __used, const char *prefix __used)<br />
{<br />
	setup_pager();<br />
	print_events();<br />
	return 0;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-list-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-help.c</title>
		<link>http://lynyrd.ru/builtin-help-c</link>
		<comments>http://lynyrd.ru/builtin-help-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:25:45 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/builtin-help-c</guid>
		<description><![CDATA[/*
 * builtin-help.c
 *
 * Builtin help command
 */
#include &#171;perf.h&#187;
#include &#171;util/cache.h&#187;
#include &#171;builtin.h&#187;
#include &#171;util/exec_cmd.h&#187;
#include &#171;common-cmds.h&#187;
#include &#171;util/parse-options.h&#187;
#include &#171;util/run-command.h&#187;
#include &#171;util/help.h&#187;
static struct man_viewer_list {
	struct man_viewer_list *next;
	char name[FLEX_ARRAY];
} *man_viewer_list;
static struct man_viewer_info_list {
	struct man_viewer_info_list *next;
	const char *info;
	char name[FLEX_ARRAY];
} *man_viewer_info_list;
enum help_format {
	HELP_FORMAT_MAN,
	HELP_FORMAT_INFO,
	HELP_FORMAT_WEB,
};
static int show_all = 0;
static enum help_format help_format = HELP_FORMAT_MAN;
static struct option builtin_help_options[] = {
	OPT_BOOLEAN(&#8217;a', &#171;all&#187;, &#038;show_all, &#171;print all available commands&#187;),
	OPT_SET_INT(&#8217;m', ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-help.c<span id="more-599"></span><br />
 *<br />
 * Builtin help command<br />
 */<br />
#include &laquo;perf.h&raquo;<br />
#include &laquo;util/cache.h&raquo;<br />
#include &laquo;builtin.h&raquo;<br />
#include &laquo;util/exec_cmd.h&raquo;<br />
#include &laquo;common-cmds.h&raquo;<br />
#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/run-command.h&raquo;<br />
#include &laquo;util/help.h&raquo;</p>
<p>static struct man_viewer_list {<br />
	struct man_viewer_list *next;<br />
	char name[FLEX_ARRAY];<br />
} *man_viewer_list;</p>
<p>static struct man_viewer_info_list {<br />
	struct man_viewer_info_list *next;<br />
	const char *info;<br />
	char name[FLEX_ARRAY];<br />
} *man_viewer_info_list;</p>
<p>enum help_format {<br />
	HELP_FORMAT_MAN,<br />
	HELP_FORMAT_INFO,<br />
	HELP_FORMAT_WEB,<br />
};</p>
<p>static int show_all = 0;<br />
static enum help_format help_format = HELP_FORMAT_MAN;<br />
static struct option builtin_help_options[] = {<br />
	OPT_BOOLEAN(&#8217;a', &laquo;all&raquo;, &#038;show_all, &laquo;print all available commands&raquo;),<br />
	OPT_SET_INT(&#8217;m', &laquo;man&raquo;, &#038;help_format, &laquo;show man page&raquo;, HELP_FORMAT_MAN),<br />
	OPT_SET_INT(&#8217;w', &laquo;web&raquo;, &#038;help_format, &laquo;show manual in web browser&raquo;,<br />
			HELP_FORMAT_WEB),<br />
	OPT_SET_INT(&#8217;i', &laquo;info&raquo;, &#038;help_format, &laquo;show info page&raquo;,<br />
			HELP_FORMAT_INFO),<br />
	OPT_END(),<br />
};</p>
<p>static const char * const builtin_help_usage[] = {<br />
	&laquo;perf help [--all] [--man|--web|--info] [command]&laquo;,<br />
	NULL<br />
};</p>
<p>static enum help_format parse_help_format(const char *format)<br />
{<br />
	if (!strcmp(format, &laquo;man&raquo;))<br />
		return HELP_FORMAT_MAN;<br />
	if (!strcmp(format, &laquo;info&raquo;))<br />
		return HELP_FORMAT_INFO;<br />
	if (!strcmp(format, &laquo;web&raquo;) || !strcmp(format, &laquo;html&raquo;))<br />
		return HELP_FORMAT_WEB;<br />
	die(&raquo;unrecognized help format &#8216;%s&#8217;&raquo;, format);<br />
}</p>
<p>static const char *get_man_viewer_info(const char *name)<br />
{<br />
	struct man_viewer_info_list *viewer;</p>
<p>	for (viewer = man_viewer_info_list; viewer; viewer = viewer->next)<br />
	{<br />
		if (!strcasecmp(name, viewer->name))<br />
			return viewer->info;<br />
	}<br />
	return NULL;<br />
}</p>
<p>static int check_emacsclient_version(void)<br />
{<br />
	struct strbuf buffer = STRBUF_INIT;<br />
	struct child_process ec_process;<br />
	const char *argv_ec[] = { &laquo;emacsclient&raquo;, &laquo;&#8211;version&raquo;, NULL };<br />
	int version;</p>
<p>	/* emacsclient prints its version number on stderr */<br />
	memset(&#038;ec_process, 0, sizeof(ec_process));<br />
	ec_process.argv = argv_ec;<br />
	ec_process.err = -1;<br />
	ec_process.stdout_to_stderr = 1;<br />
	if (start_command(&#038;ec_process)) {<br />
		fprintf(stderr, &laquo;Failed to start emacsclient.\n&raquo;);<br />
		return -1;<br />
	}<br />
	strbuf_read(&#038;buffer, ec_process.err, 20);<br />
	close(ec_process.err);</p>
<p>	/*<br />
	 * Don&#8217;t bother checking return value, because &laquo;emacsclient &#8211;version&raquo;<br />
	 * seems to always exits with code 1.<br />
	 */<br />
	finish_command(&#038;ec_process);</p>
<p>	if (prefixcmp(buffer.buf, &laquo;emacsclient&raquo;)) {<br />
		fprintf(stderr, &laquo;Failed to parse emacsclient version.\n&raquo;);<br />
		strbuf_release(&#038;buffer);<br />
		return -1;<br />
	}</p>
<p>	strbuf_remove(&#038;buffer, 0, strlen(&raquo;emacsclient&raquo;));<br />
	version = atoi(buffer.buf);</p>
<p>	if (version < 22) {<br />
		fprintf(stderr,<br />
			"emacsclient version '%d' too old (< 22).\n",<br />
			version);<br />
		strbuf_release(&#038;buffer);<br />
		return -1;<br />
	}</p>
<p>	strbuf_release(&#038;buffer);<br />
	return 0;<br />
}</p>
<p>static void exec_woman_emacs(const char* path, const char *page)<br />
{<br />
	if (!check_emacsclient_version()) {<br />
		/* This works only with emacsclient version >= 22. */<br />
		struct strbuf man_page = STRBUF_INIT;</p>
<p>		if (!path)<br />
			path = &laquo;emacsclient&raquo;;<br />
		strbuf_addf(&#038;man_page, &laquo;(woman \&raquo;%s\&raquo;)&raquo;, page);<br />
		execlp(path, &laquo;emacsclient&raquo;, &laquo;-e&raquo;, man_page.buf, NULL);<br />
		warning(&raquo;failed to exec &#8216;%s&#8217;: %s&raquo;, path, strerror(errno));<br />
	}<br />
}</p>
<p>static void exec_man_konqueror(const char* path, const char *page)<br />
{<br />
	const char *display = getenv(&raquo;DISPLAY&raquo;);<br />
	if (display &#038;&#038; *display) {<br />
		struct strbuf man_page = STRBUF_INIT;<br />
		const char *filename = &laquo;kfmclient&raquo;;</p>
<p>		/* It&#8217;s simpler to launch konqueror using kfmclient. */<br />
		if (path) {<br />
			const char *file = strrchr(path, &#8216;/&#8217;);<br />
			if (file &#038;&#038; !strcmp(file + 1, &laquo;konqueror&raquo;)) {<br />
				char *new = strdup(path);<br />
				char *dest = strrchr(new, &#8216;/&#8217;);</p>
<p>				/* strlen(&raquo;konqueror&raquo;) == strlen(&raquo;kfmclient&raquo;) */<br />
				strcpy(dest + 1, &laquo;kfmclient&raquo;);<br />
				path = new;<br />
			}<br />
			if (file)<br />
				filename = file;<br />
		} else<br />
			path = &laquo;kfmclient&raquo;;<br />
		strbuf_addf(&#038;man_page, &laquo;man:%s(1)&raquo;, page);<br />
		execlp(path, filename, &laquo;newTab&raquo;, man_page.buf, NULL);<br />
		warning(&raquo;failed to exec &#8216;%s&#8217;: %s&raquo;, path, strerror(errno));<br />
	}<br />
}</p>
<p>static void exec_man_man(const char* path, const char *page)<br />
{<br />
	if (!path)<br />
		path = &laquo;man&raquo;;<br />
	execlp(path, &laquo;man&raquo;, page, NULL);<br />
	warning(&raquo;failed to exec &#8216;%s&#8217;: %s&raquo;, path, strerror(errno));<br />
}</p>
<p>static void exec_man_cmd(const char *cmd, const char *page)<br />
{<br />
	struct strbuf shell_cmd = STRBUF_INIT;<br />
	strbuf_addf(&#038;shell_cmd, &laquo;%s %s&raquo;, cmd, page);<br />
	execl(&raquo;/bin/sh&raquo;, &laquo;sh&raquo;, &laquo;-c&raquo;, shell_cmd.buf, NULL);<br />
	warning(&raquo;failed to exec &#8216;%s&#8217;: %s&raquo;, cmd, strerror(errno));<br />
}</p>
<p>static void add_man_viewer(const char *name)<br />
{<br />
	struct man_viewer_list **p = &#038;man_viewer_list;<br />
	size_t len = strlen(name);</p>
<p>	while (*p)<br />
		p = &#038;((*p)->next);<br />
	*p = calloc(1, (sizeof(**p) + len + 1));<br />
	strncpy((*p)->name, name, len);<br />
}</p>
<p>static int supported_man_viewer(const char *name, size_t len)<br />
{<br />
	return (!strncasecmp(&raquo;man&raquo;, name, len) ||<br />
		!strncasecmp(&raquo;woman&raquo;, name, len) ||<br />
		!strncasecmp(&raquo;konqueror&raquo;, name, len));<br />
}</p>
<p>static void do_add_man_viewer_info(const char *name,<br />
				   size_t len,<br />
				   const char *value)<br />
{<br />
	struct man_viewer_info_list *new = calloc(1, sizeof(*new) + len + 1);</p>
<p>	strncpy(new->name, name, len);<br />
	new->info = strdup(value);<br />
	new->next = man_viewer_info_list;<br />
	man_viewer_info_list = new;<br />
}</p>
<p>static int add_man_viewer_path(const char *name,<br />
			       size_t len,<br />
			       const char *value)<br />
{<br />
	if (supported_man_viewer(name, len))<br />
		do_add_man_viewer_info(name, len, value);<br />
	else<br />
		warning(&raquo;&#8216;%s&#8217;: path for unsupported man viewer.\n&raquo;<br />
			&laquo;Please consider using &#8216;man.<tool>.cmd&#8217; instead.&raquo;,<br />
			name);</p>
<p>	return 0;<br />
}</p>
<p>static int add_man_viewer_cmd(const char *name,<br />
			      size_t len,<br />
			      const char *value)<br />
{<br />
	if (supported_man_viewer(name, len))<br />
		warning(&raquo;&#8216;%s&#8217;: cmd for supported man viewer.\n&raquo;<br />
			&laquo;Please consider using &#8216;man.<tool>.path&#8217; instead.&raquo;,<br />
			name);<br />
	else<br />
		do_add_man_viewer_info(name, len, value);</p>
<p>	return 0;<br />
}</p>
<p>static int add_man_viewer_info(const char *var, const char *value)<br />
{<br />
	const char *name = var + 4;<br />
	const char *subkey = strrchr(name, &#8216;.&#8217;);</p>
<p>	if (!subkey)<br />
		return error(&raquo;Config with no key for man viewer: %s&raquo;, name);</p>
<p>	if (!strcmp(subkey, &laquo;.path&raquo;)) {<br />
		if (!value)<br />
			return config_error_nonbool(var);<br />
		return add_man_viewer_path(name, subkey &#8211; name, value);<br />
	}<br />
	if (!strcmp(subkey, &laquo;.cmd&raquo;)) {<br />
		if (!value)<br />
			return config_error_nonbool(var);<br />
		return add_man_viewer_cmd(name, subkey &#8211; name, value);<br />
	}</p>
<p>	warning(&raquo;&#8216;%s&#8217;: unsupported man viewer sub key.&raquo;, subkey);<br />
	return 0;<br />
}</p>
<p>static int perf_help_config(const char *var, const char *value, void *cb)<br />
{<br />
	if (!strcmp(var, &laquo;help.format&raquo;)) {<br />
		if (!value)<br />
			return config_error_nonbool(var);<br />
		help_format = parse_help_format(value);<br />
		return 0;<br />
	}<br />
	if (!strcmp(var, &laquo;man.viewer&raquo;)) {<br />
		if (!value)<br />
			return config_error_nonbool(var);<br />
		add_man_viewer(value);<br />
		return 0;<br />
	}<br />
	if (!prefixcmp(var, &laquo;man.&raquo;))<br />
		return add_man_viewer_info(var, value);</p>
<p>	return perf_default_config(var, value, cb);<br />
}</p>
<p>static struct cmdnames main_cmds, other_cmds;</p>
<p>void list_common_cmds_help(void)<br />
{<br />
	unsigned int i, longest = 0;</p>
<p>	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {<br />
		if (longest < strlen(common_cmds[i].name))<br />
			longest = strlen(common_cmds[i].name);<br />
	}</p>
<p>	puts(" The most commonly used perf commands are:");<br />
	for (i = 0; i < ARRAY_SIZE(common_cmds); i++) {<br />
		printf("   %s   ", common_cmds[i].name);<br />
		mput_char(' ', longest - strlen(common_cmds[i].name));<br />
		puts(common_cmds[i].help);<br />
	}<br />
}</p>
<p>static int is_perf_command(const char *s)<br />
{<br />
	return is_in_cmdlist(&#038;main_cmds, s) ||<br />
		is_in_cmdlist(&#038;other_cmds, s);<br />
}</p>
<p>static const char *prepend(const char *prefix, const char *cmd)<br />
{<br />
	size_t pre_len = strlen(prefix);<br />
	size_t cmd_len = strlen(cmd);<br />
	char *p = malloc(pre_len + cmd_len + 1);<br />
	memcpy(p, prefix, pre_len);<br />
	strcpy(p + pre_len, cmd);<br />
	return p;<br />
}</p>
<p>static const char *cmd_to_page(const char *perf_cmd)<br />
{<br />
	if (!perf_cmd)<br />
		return "perf";<br />
	else if (!prefixcmp(perf_cmd, "perf"))<br />
		return perf_cmd;<br />
	else if (is_perf_command(perf_cmd))<br />
		return prepend("perf-", perf_cmd);<br />
	else<br />
		return prepend("perf-", perf_cmd);<br />
}</p>
<p>static void setup_man_path(void)<br />
{<br />
	struct strbuf new_path = STRBUF_INIT;<br />
	const char *old_path = getenv("MANPATH");</p>
<p>	/* We should always put ':' after our path. If there is no<br />
	 * old_path, the ':' at the end will let 'man' to try<br />
	 * system-wide paths after ours to find the manual page. If<br />
	 * there is old_path, we need ':' as delimiter. */<br />
	strbuf_addstr(&#038;new_path, system_path(PERF_MAN_PATH));<br />
	strbuf_addch(&#038;new_path, ':');<br />
	if (old_path)<br />
		strbuf_addstr(&#038;new_path, old_path);</p>
<p>	setenv("MANPATH", new_path.buf, 1);</p>
<p>	strbuf_release(&#038;new_path);<br />
}</p>
<p>static void exec_viewer(const char *name, const char *page)<br />
{<br />
	const char *info = get_man_viewer_info(name);</p>
<p>	if (!strcasecmp(name, "man"))<br />
		exec_man_man(info, page);<br />
	else if (!strcasecmp(name, "woman"))<br />
		exec_woman_emacs(info, page);<br />
	else if (!strcasecmp(name, "konqueror"))<br />
		exec_man_konqueror(info, page);<br />
	else if (info)<br />
		exec_man_cmd(info, page);<br />
	else<br />
		warning("'%s': unknown man viewer.", name);<br />
}</p>
<p>static void show_man_page(const char *perf_cmd)<br />
{<br />
	struct man_viewer_list *viewer;<br />
	const char *page = cmd_to_page(perf_cmd);<br />
	const char *fallback = getenv("PERF_MAN_VIEWER");</p>
<p>	setup_man_path();<br />
	for (viewer = man_viewer_list; viewer; viewer = viewer->next)<br />
	{<br />
		exec_viewer(viewer->name, page); /* will return when unable */<br />
	}<br />
	if (fallback)<br />
		exec_viewer(fallback, page);<br />
	exec_viewer(&raquo;man&raquo;, page);<br />
	die(&raquo;no man viewer handled the request&raquo;);<br />
}</p>
<p>static void show_info_page(const char *perf_cmd)<br />
{<br />
	const char *page = cmd_to_page(perf_cmd);<br />
	setenv(&raquo;INFOPATH&raquo;, system_path(PERF_INFO_PATH), 1);<br />
	execlp(&raquo;info&raquo;, &laquo;info&raquo;, &laquo;perfman&raquo;, page, NULL);<br />
}</p>
<p>static void get_html_page_path(struct strbuf *page_path, const char *page)<br />
{<br />
	struct stat st;<br />
	const char *html_path = system_path(PERF_HTML_PATH);</p>
<p>	/* Check that we have a perf documentation directory. */<br />
	if (stat(mkpath(&raquo;%s/perf.html&raquo;, html_path), &#038;st)<br />
	    || !S_ISREG(st.st_mode))<br />
		die(&raquo;&#8216;%s&#8217;: not a documentation directory.&raquo;, html_path);</p>
<p>	strbuf_init(page_path, 0);<br />
	strbuf_addf(page_path, &laquo;%s/%s.html&raquo;, html_path, page);<br />
}</p>
<p>/*<br />
 * If open_html is not defined in a platform-specific way (see for<br />
 * example compat/mingw.h), we use the script web&#8211;browse to display<br />
 * HTML.<br />
 */<br />
#ifndef open_html<br />
static void open_html(const char *path)<br />
{<br />
	execl_perf_cmd(&raquo;web&#8211;browse&raquo;, &laquo;-c&raquo;, &laquo;help.browser&raquo;, path, NULL);<br />
}<br />
#endif</p>
<p>static void show_html_page(const char *perf_cmd)<br />
{<br />
	const char *page = cmd_to_page(perf_cmd);<br />
	struct strbuf page_path; /* it leaks but we exec bellow */</p>
<p>	get_html_page_path(&#038;page_path, page);</p>
<p>	open_html(page_path.buf);<br />
}</p>
<p>int cmd_help(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	const char *alias;</p>
<p>	load_command_list(&raquo;perf-&raquo;, &#038;main_cmds, &#038;other_cmds);</p>
<p>	perf_config(perf_help_config, NULL);</p>
<p>	argc = parse_options(argc, argv, builtin_help_options,<br />
			builtin_help_usage, 0);</p>
<p>	if (show_all) {<br />
		printf(&raquo;\n usage: %s\n\n&raquo;, perf_usage_string);<br />
		list_commands(&raquo;perf commands&raquo;, &#038;main_cmds, &#038;other_cmds);<br />
		printf(&raquo; %s\n\n&raquo;, perf_more_info_string);<br />
		return 0;<br />
	}</p>
<p>	if (!argv[0]) {<br />
		printf(&raquo;\n usage: %s\n\n&raquo;, perf_usage_string);<br />
		list_common_cmds_help();<br />
		printf(&raquo;\n %s\n\n&raquo;, perf_more_info_string);<br />
		return 0;<br />
	}</p>
<p>	alias = alias_lookup(argv[0]);<br />
	if (alias &#038;&#038; !is_perf_command(argv[0])) {<br />
		printf(&raquo;`perf %s&#8217; is aliased to `%s&#8217;\n&raquo;, argv[0], alias);<br />
		return 0;<br />
	}</p>
<p>	switch (help_format) {<br />
	case HELP_FORMAT_MAN:<br />
		show_man_page(argv[0]);<br />
		break;<br />
	case HELP_FORMAT_INFO:<br />
		show_info_page(argv[0]);<br />
		break;<br />
	case HELP_FORMAT_WEB:<br />
		show_html_page(argv[0]);<br />
	default:<br />
		break;<br />
	}</p>
<p>	return 0;<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-help-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>builtin-annotate.c</title>
		<link>http://lynyrd.ru/builtin-annotate-c</link>
		<comments>http://lynyrd.ru/builtin-annotate-c#comments</comments>
		<pubDate>Wed, 13 Jan 2010 13:25:25 +0000</pubDate>
		<dc:creator>lynyrd</dc:creator>
				<category><![CDATA[perf]]></category>

		<guid isPermaLink="false">http://lynyrd.ru/?p=597</guid>
		<description><![CDATA[/*
 * builtin-annotate.c
 *
 * Builtin annotate command: Analyze the perf.data input file,
 * look up and read DSOs and symbol information and display
 * a histogram of results, along various sorting keys.
 */
#include &#171;builtin.h&#187;
#include &#171;util/util.h&#187;
#include &#171;util/color.h&#187;
#include

#include &#171;util/cache.h&#187;
#include

#include &#171;util/symbol.h&#187;
#include &#171;util/string.h&#187;
#include &#171;perf.h&#187;
#include &#171;util/debug.h&#187;
#include &#171;util/parse-options.h&#187;
#include &#171;util/parse-events.h&#187;
#include &#171;util/thread.h&#187;
static char		const *input_name = &#171;perf.data&#187;;
static char		default_sort_order[] = &#171;comm,symbol&#187;;
static char		*sort_order = default_sort_order;
static ]]></description>
			<content:encoded><![CDATA[<p>/*<br />
 * builtin-annotate.c<span id="more-597"></span><br />
 *<br />
 * Builtin annotate command: Analyze the perf.data input file,<br />
 * look up and read DSOs and symbol information and display<br />
 * a histogram of results, along various sorting keys.<br />
 */<br />
#include &laquo;builtin.h&raquo;</p>
<p>#include &laquo;util/util.h&raquo;</p>
<p>#include &laquo;util/color.h&raquo;<br />
#include
<linux/list.h>
#include &laquo;util/cache.h&raquo;<br />
#include
<linux/rbtree.h>
#include &laquo;util/symbol.h&raquo;<br />
#include &laquo;util/string.h&raquo;</p>
<p>#include &laquo;perf.h&raquo;<br />
#include &laquo;util/debug.h&raquo;</p>
<p>#include &laquo;util/parse-options.h&raquo;<br />
#include &laquo;util/parse-events.h&raquo;<br />
#include &laquo;util/thread.h&raquo;</p>
<p>static char		const *input_name = &laquo;perf.data&raquo;;</p>
<p>static char		default_sort_order[] = &laquo;comm,symbol&raquo;;<br />
static char		*sort_order = default_sort_order;</p>
<p>static int		force;<br />
static int		input;<br />
static int		show_mask = SHOW_KERNEL | SHOW_USER | SHOW_HV;</p>
<p>static int		full_paths;</p>
<p>static int		print_line;</p>
<p>static unsigned long	page_size;<br />
static unsigned long	mmap_window = 32;</p>
<p>static struct rb_root	threads;<br />
static struct thread	*last_match;</p>
<p>struct sym_ext {<br />
	struct rb_node	node;<br />
	double		percent;<br />
	char		*path;<br />
};</p>
<p>/*<br />
 * histogram, sorted on item, collects counts<br />
 */</p>
<p>static struct rb_root hist;</p>
<p>struct hist_entry {<br />
	struct rb_node	 rb_node;</p>
<p>	struct thread	 *thread;<br />
	struct map	 *map;<br />
	struct dso	 *dso;<br />
	struct symbol	 *sym;<br />
	u64	 ip;<br />
	char		 level;</p>
<p>	uint32_t	 count;<br />
};</p>
<p>/*<br />
 * configurable sorting bits<br />
 */</p>
<p>struct sort_entry {<br />
	struct list_head list;</p>
<p>	const char *header;</p>
<p>	int64_t (*cmp)(struct hist_entry *, struct hist_entry *);<br />
	int64_t (*collapse)(struct hist_entry *, struct hist_entry *);<br />
	size_t	(*print)(FILE *fp, struct hist_entry *);<br />
};</p>
<p>/* &#8211;sort pid */</p>
<p>static int64_t<br />
sort__thread_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	return right->thread->pid &#8211; left->thread->pid;<br />
}</p>
<p>static size_t<br />
sort__thread_print(FILE *fp, struct hist_entry *self)<br />
{<br />
	return fprintf(fp, &laquo;%16s:%5d&raquo;, self->thread->comm ?: &laquo;&raquo;, self->thread->pid);<br />
}</p>
<p>static struct sort_entry sort_thread = {<br />
	.header = &raquo;         Command:  Pid&raquo;,<br />
	.cmp	= sort__thread_cmp,<br />
	.print	= sort__thread_print,<br />
};</p>
<p>/* &#8211;sort comm */</p>
<p>static int64_t<br />
sort__comm_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	return right->thread->pid &#8211; left->thread->pid;<br />
}</p>
<p>static int64_t<br />
sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	char *comm_l = left->thread->comm;<br />
	char *comm_r = right->thread->comm;</p>
<p>	if (!comm_l || !comm_r) {<br />
		if (!comm_l &#038;&#038; !comm_r)<br />
			return 0;<br />
		else if (!comm_l)<br />
			return -1;<br />
		else<br />
			return 1;<br />
	}</p>
<p>	return strcmp(comm_l, comm_r);<br />
}</p>
<p>static size_t<br />
sort__comm_print(FILE *fp, struct hist_entry *self)<br />
{<br />
	return fprintf(fp, &laquo;%16s&raquo;, self->thread->comm);<br />
}</p>
<p>static struct sort_entry sort_comm = {<br />
	.header		= &raquo;         Command&raquo;,<br />
	.cmp		= sort__comm_cmp,<br />
	.collapse	= sort__comm_collapse,<br />
	.print		= sort__comm_print,<br />
};</p>
<p>/* &#8211;sort dso */</p>
<p>static int64_t<br />
sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct dso *dso_l = left->dso;<br />
	struct dso *dso_r = right->dso;</p>
<p>	if (!dso_l || !dso_r) {<br />
		if (!dso_l &#038;&#038; !dso_r)<br />
			return 0;<br />
		else if (!dso_l)<br />
			return -1;<br />
		else<br />
			return 1;<br />
	}</p>
<p>	return strcmp(dso_l->name, dso_r->name);<br />
}</p>
<p>static size_t<br />
sort__dso_print(FILE *fp, struct hist_entry *self)<br />
{<br />
	if (self->dso)<br />
		return fprintf(fp, &laquo;%-25s&raquo;, self->dso->name);</p>
<p>	return fprintf(fp, &laquo;%016llx         &laquo;, (u64)self->ip);<br />
}</p>
<p>static struct sort_entry sort_dso = {<br />
	.header = &laquo;Shared Object            &laquo;,<br />
	.cmp	= sort__dso_cmp,<br />
	.print	= sort__dso_print,<br />
};</p>
<p>/* &#8211;sort symbol */</p>
<p>static int64_t<br />
sort__sym_cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	u64 ip_l, ip_r;</p>
<p>	if (left->sym == right->sym)<br />
		return 0;</p>
<p>	ip_l = left->sym ? left->sym->start : left->ip;<br />
	ip_r = right->sym ? right->sym->start : right->ip;</p>
<p>	return (int64_t)(ip_r &#8211; ip_l);<br />
}</p>
<p>static size_t<br />
sort__sym_print(FILE *fp, struct hist_entry *self)<br />
{<br />
	size_t ret = 0;</p>
<p>	if (verbose)<br />
		ret += fprintf(fp, &laquo;%#018llx  &laquo;, (u64)self->ip);</p>
<p>	if (self->sym) {<br />
		ret += fprintf(fp, &laquo;[%c] %s&raquo;,<br />
			self->dso == kernel_dso ? &#8216;k&#8217; : &#8216;.&#8217;, self->sym->name);<br />
	} else {<br />
		ret += fprintf(fp, &laquo;%#016llx&raquo;, (u64)self->ip);<br />
	}</p>
<p>	return ret;<br />
}</p>
<p>static struct sort_entry sort_sym = {<br />
	.header = &laquo;Symbol&raquo;,<br />
	.cmp	= sort__sym_cmp,<br />
	.print	= sort__sym_print,<br />
};</p>
<p>static int sort__need_collapse = 0;</p>
<p>struct sort_dimension {<br />
	const char		*name;<br />
	struct sort_entry	*entry;<br />
	int			taken;<br />
};</p>
<p>static struct sort_dimension sort_dimensions[] = {<br />
	{ .name = &laquo;pid&raquo;,	.entry = &#038;sort_thread,	},<br />
	{ .name = &laquo;comm&raquo;,	.entry = &#038;sort_comm,	},<br />
	{ .name = &laquo;dso&raquo;,	.entry = &#038;sort_dso,	},<br />
	{ .name = &laquo;symbol&raquo;,	.entry = &#038;sort_sym,	},<br />
};</p>
<p>static LIST_HEAD(hist_entry__sort_list);</p>
<p>static int sort_dimension__add(char *tok)<br />
{<br />
	unsigned int i;</p>
<p>	for (i = 0; i < ARRAY_SIZE(sort_dimensions); i++) {<br />
		struct sort_dimension *sd = &#038;sort_dimensions[i];</p>
<p>		if (sd->taken)<br />
			continue;</p>
<p>		if (strncasecmp(tok, sd->name, strlen(tok)))<br />
			continue;</p>
<p>		if (sd->entry->collapse)<br />
			sort__need_collapse = 1;</p>
<p>		list_add_tail(&#038;sd->entry->list, &#038;hist_entry__sort_list);<br />
		sd->taken = 1;</p>
<p>		return 0;<br />
	}</p>
<p>	return -ESRCH;<br />
}</p>
<p>static int64_t<br />
hist_entry__cmp(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct sort_entry *se;<br />
	int64_t cmp = 0;</p>
<p>	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		cmp = se->cmp(left, right);<br />
		if (cmp)<br />
			break;<br />
	}</p>
<p>	return cmp;<br />
}</p>
<p>static int64_t<br />
hist_entry__collapse(struct hist_entry *left, struct hist_entry *right)<br />
{<br />
	struct sort_entry *se;<br />
	int64_t cmp = 0;</p>
<p>	list_for_each_entry(se, &#038;hist_entry__sort_list, list) {<br />
		int64_t (*f)(struct hist_entry *, struct hist_entry *);</p>
<p>		f = se->collapse ?: se->cmp;</p>
<p>		cmp = f(left, right);<br />
		if (cmp)<br />
			break;<br />
	}</p>
<p>	return cmp;<br />
}</p>
<p>/*<br />
 * collect histogram counts<br />
 */<br />
static void hist_hit(struct hist_entry *he, u64 ip)<br />
{<br />
	unsigned int sym_size, offset;<br />
	struct symbol *sym = he->sym;</p>
<p>	he->count++;</p>
<p>	if (!sym || !sym->hist)<br />
		return;</p>
<p>	sym_size = sym->end &#8211; sym->start;<br />
	offset = ip &#8211; sym->start;</p>
<p>	if (offset >= sym_size)<br />
		return;</p>
<p>	sym->hist_sum++;<br />
	sym->hist[offset]++;</p>
<p>	if (verbose >= 3)<br />
		printf(&raquo;%p %s: count++ [ip: %p, %08Lx] => %Ld\n&raquo;,<br />
			(void *)(unsigned long)he->sym->start,<br />
			he->sym->name,<br />
			(void *)(unsigned long)ip, ip &#8211; he->sym->start,<br />
			sym->hist[offset]);<br />
}</p>
<p>static int<br />
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,<br />
		struct symbol *sym, u64 ip, char level)<br />
{<br />
	struct rb_node **p = &#038;hist.rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct hist_entry *he;<br />
	struct hist_entry entry = {<br />
		.thread	= thread,<br />
		.map	= map,<br />
		.dso	= dso,<br />
		.sym	= sym,<br />
		.ip	= ip,<br />
		.level	= level,<br />
		.count	= 1,<br />
	};<br />
	int cmp;</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		he = rb_entry(parent, struct hist_entry, rb_node);</p>
<p>		cmp = hist_entry__cmp(&#038;entry, he);</p>
<p>		if (!cmp) {<br />
			hist_hit(he, ip);</p>
<p>			return 0;<br />
		}</p>
<p>		if (cmp < 0)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	he = malloc(sizeof(*he));<br />
	if (!he)<br />
		return -ENOMEM;<br />
	*he = entry;<br />
	rb_link_node(&#038;he->rb_node, parent, p);<br />
	rb_insert_color(&#038;he->rb_node, &#038;hist);</p>
<p>	return 0;<br />
}</p>
<p>static void hist_entry__free(struct hist_entry *he)<br />
{<br />
	free(he);<br />
}</p>
<p>/*<br />
 * collapse the histogram<br />
 */</p>
<p>static struct rb_root collapse_hists;</p>
<p>static void collapse__insert_entry(struct hist_entry *he)<br />
{<br />
	struct rb_node **p = &#038;collapse_hists.rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct hist_entry *iter;<br />
	int64_t cmp;</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		iter = rb_entry(parent, struct hist_entry, rb_node);</p>
<p>		cmp = hist_entry__collapse(iter, he);</p>
<p>		if (!cmp) {<br />
			iter->count += he->count;<br />
			hist_entry__free(he);<br />
			return;<br />
		}</p>
<p>		if (cmp < 0)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;he->rb_node, parent, p);<br />
	rb_insert_color(&#038;he->rb_node, &#038;collapse_hists);<br />
}</p>
<p>static void collapse__resort(void)<br />
{<br />
	struct rb_node *next;<br />
	struct hist_entry *n;</p>
<p>	if (!sort__need_collapse)<br />
		return;</p>
<p>	next = rb_first(&#038;hist);<br />
	while (next) {<br />
		n = rb_entry(next, struct hist_entry, rb_node);<br />
		next = rb_next(&#038;n->rb_node);</p>
<p>		rb_erase(&#038;n->rb_node, &#038;hist);<br />
		collapse__insert_entry(n);<br />
	}<br />
}</p>
<p>/*<br />
 * reverse the map, sort on count.<br />
 */</p>
<p>static struct rb_root output_hists;</p>
<p>static void output__insert_entry(struct hist_entry *he)<br />
{<br />
	struct rb_node **p = &#038;output_hists.rb_node;<br />
	struct rb_node *parent = NULL;<br />
	struct hist_entry *iter;</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		iter = rb_entry(parent, struct hist_entry, rb_node);</p>
<p>		if (he->count > iter->count)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;he->rb_node, parent, p);<br />
	rb_insert_color(&#038;he->rb_node, &#038;output_hists);<br />
}</p>
<p>static void output__resort(void)<br />
{<br />
	struct rb_node *next;<br />
	struct hist_entry *n;<br />
	struct rb_root *tree = &hist;</p>
<p>	if (sort__need_collapse)<br />
		tree = &#038;collapse_hists;</p>
<p>	next = rb_first(tree);</p>
<p>	while (next) {<br />
		n = rb_entry(next, struct hist_entry, rb_node);<br />
		next = rb_next(&#038;n->rb_node);</p>
<p>		rb_erase(&#038;n->rb_node, tree);<br />
		output__insert_entry(n);<br />
	}<br />
}</p>
<p>static unsigned long total = 0,<br />
		     total_mmap = 0,<br />
		     total_comm = 0,<br />
		     total_fork = 0,<br />
		     total_unknown = 0;</p>
<p>static int<br />
process_sample_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	char level;<br />
	int show = 0;<br />
	struct dso *dso = NULL;<br />
	struct thread *thread;<br />
	u64 ip = event->ip.ip;<br />
	struct map *map = NULL;</p>
<p>	thread = threads__findnew(event->ip.pid, &#038;threads, &#038;last_match);</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_EVENT (IP, %d): %d: %p\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->header.misc,<br />
		event->ip.pid,<br />
		(void *)(long)ip);</p>
<p>	dump_printf(&raquo; &#8230; thread: %s:%d\n&raquo;, thread->comm, thread->pid);</p>
<p>	if (thread == NULL) {<br />
		fprintf(stderr, &laquo;problem processing %d event, skipping it.\n&raquo;,<br />
			event->header.type);<br />
		return -1;<br />
	}</p>
<p>	if (event->header.misc &#038; PERF_RECORD_MISC_KERNEL) {<br />
		show = SHOW_KERNEL;<br />
		level = &#8216;k&#8217;;</p>
<p>		dso = kernel_dso;</p>
<p>		dump_printf(&raquo; &#8230;&#8230; dso: %s\n&raquo;, dso->name);</p>
<p>	} else if (event->header.misc &#038; PERF_RECORD_MISC_USER) {</p>
<p>		show = SHOW_USER;<br />
		level = &#8216;.&#8217;;</p>
<p>		map = thread__find_map(thread, ip);<br />
		if (map != NULL) {<br />
			ip = map->map_ip(map, ip);<br />
			dso = map->dso;<br />
		} else {<br />
			/*<br />
			 * If this is outside of all known maps,<br />
			 * and is a negative address, try to look it<br />
			 * up in the kernel dso, as it might be a<br />
			 * vsyscall (which executes in user-mode):<br />
			 */<br />
			if ((long long)ip < 0)<br />
				dso = kernel_dso;<br />
		}<br />
		dump_printf(" ...... dso: %s\n", dso ? dso->name : &laquo;<not found>&laquo;);</p>
<p>	} else {<br />
		show = SHOW_HV;<br />
		level = &#8216;H&#8217;;<br />
		dump_printf(&raquo; &#8230;&#8230; dso: [hypervisor]\n&raquo;);<br />
	}</p>
<p>	if (show &#038; show_mask) {<br />
		struct symbol *sym = NULL;</p>
<p>		if (dso)<br />
			sym = dso->find_symbol(dso, ip);</p>
<p>		if (hist_entry__add(thread, map, dso, sym, ip, level)) {<br />
			fprintf(stderr,<br />
		&laquo;problem incrementing symbol count, skipping event\n&raquo;);<br />
			return -1;<br />
		}<br />
	}<br />
	total++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_mmap_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;<br />
	struct map *map = map__new(&#038;event->mmap, NULL, 0);</p>
<p>	thread = threads__findnew(event->mmap.pid, &#038;threads, &#038;last_match);</p>
<p>	dump_printf(&raquo;%p [%p]: PERF_RECORD_MMAP %d: [%p(%p) @ %p]: %s\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->mmap.pid,<br />
		(void *)(long)event->mmap.start,<br />
		(void *)(long)event->mmap.len,<br />
		(void *)(long)event->mmap.pgoff,<br />
		event->mmap.filename);</p>
<p>	if (thread == NULL || map == NULL) {<br />
		dump_printf(&raquo;problem processing PERF_RECORD_MMAP, skipping event.\n&raquo;);<br />
		return 0;<br />
	}</p>
<p>	thread__insert_map(thread, map);<br />
	total_mmap++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_comm_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;</p>
<p>	thread = threads__findnew(event->comm.pid, &#038;threads, &#038;last_match);<br />
	dump_printf(&raquo;%p [%p]: PERF_RECORD_COMM: %s:%d\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->comm.comm, event->comm.pid);</p>
<p>	if (thread == NULL ||<br />
	    thread__set_comm(thread, event->comm.comm)) {<br />
		dump_printf(&raquo;problem processing PERF_RECORD_COMM, skipping event.\n&raquo;);<br />
		return -1;<br />
	}<br />
	total_comm++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_fork_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	struct thread *thread;<br />
	struct thread *parent;</p>
<p>	thread = threads__findnew(event->fork.pid, &#038;threads, &#038;last_match);<br />
	parent = threads__findnew(event->fork.ppid, &#038;threads, &#038;last_match);<br />
	dump_printf(&raquo;%p [%p]: PERF_RECORD_FORK: %d:%d\n&raquo;,<br />
		(void *)(offset + head),<br />
		(void *)(long)(event->header.size),<br />
		event->fork.pid, event->fork.ppid);</p>
<p>	/*<br />
	 * A thread clone will have the same PID for both<br />
	 * parent and child.<br />
	 */<br />
	if (thread == parent)<br />
		return 0;</p>
<p>	if (!thread || !parent || thread__fork(thread, parent)) {<br />
		dump_printf(&raquo;problem processing PERF_RECORD_FORK, skipping event.\n&raquo;);<br />
		return -1;<br />
	}<br />
	total_fork++;</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
process_event(event_t *event, unsigned long offset, unsigned long head)<br />
{<br />
	switch (event->header.type) {<br />
	case PERF_RECORD_SAMPLE:<br />
		return process_sample_event(event, offset, head);</p>
<p>	case PERF_RECORD_MMAP:<br />
		return process_mmap_event(event, offset, head);</p>
<p>	case PERF_RECORD_COMM:<br />
		return process_comm_event(event, offset, head);</p>
<p>	case PERF_RECORD_FORK:<br />
		return process_fork_event(event, offset, head);<br />
	/*<br />
	 * We dont process them right now but they are fine:<br />
	 */</p>
<p>	case PERF_RECORD_THROTTLE:<br />
	case PERF_RECORD_UNTHROTTLE:<br />
		return 0;</p>
<p>	default:<br />
		return -1;<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static int<br />
parse_line(FILE *file, struct symbol *sym, u64 start, u64 len)<br />
{<br />
	char *line = NULL, *tmp, *tmp2;<br />
	static const char *prev_line;<br />
	static const char *prev_color;<br />
	unsigned int offset;<br />
	size_t line_len;<br />
	s64 line_ip;<br />
	int ret;<br />
	char *c;</p>
<p>	if (getline(&#038;line, &#038;line_len, file) < 0)<br />
		return -1;<br />
	if (!line)<br />
		return -1;</p>
<p>	c = strchr(line, '\n');<br />
	if (c)<br />
		*c = 0;</p>
<p>	line_ip = -1;<br />
	offset = 0;<br />
	ret = -2;</p>
<p>	/*<br />
	 * Strip leading spaces:<br />
	 */<br />
	tmp = line;<br />
	while (*tmp) {<br />
		if (*tmp != ' ')<br />
			break;<br />
		tmp++;<br />
	}</p>
<p>	if (*tmp) {<br />
		/*<br />
		 * Parse hexa addresses followed by ':'<br />
		 */<br />
		line_ip = strtoull(tmp, &#038;tmp2, 16);<br />
		if (*tmp2 != ':')<br />
			line_ip = -1;<br />
	}</p>
<p>	if (line_ip != -1) {<br />
		const char *path = NULL;<br />
		unsigned int hits = 0;<br />
		double percent = 0.0;<br />
		const char *color;<br />
		struct sym_ext *sym_ext = sym->priv;</p>
<p>		offset = line_ip &#8211; start;<br />
		if (offset < len)<br />
			hits = sym->hist[offset];</p>
<p>		if (offset < len &#038;&#038; sym_ext) {<br />
			path = sym_ext[offset].path;<br />
			percent = sym_ext[offset].percent;<br />
		} else if (sym->hist_sum)<br />
			percent = 100.0 * hits / sym->hist_sum;</p>
<p>		color = get_percent_color(percent);</p>
<p>		/*<br />
		 * Also color the filename and line if needed, with<br />
		 * the same color than the percentage. Don&#8217;t print it<br />
		 * twice for close colored ip with the same filename:line<br />
		 */<br />
		if (path) {<br />
			if (!prev_line || strcmp(prev_line, path)<br />
				       || color != prev_color) {<br />
				color_fprintf(stdout, color, &raquo; %s&raquo;, path);<br />
				prev_line = path;<br />
				prev_color = color;<br />
			}<br />
		}</p>
<p>		color_fprintf(stdout, color, &raquo; %7.2f&raquo;, percent);<br />
		printf(&raquo; :	&laquo;);<br />
		color_fprintf(stdout, PERF_COLOR_BLUE, &laquo;%s\n&raquo;, line);<br />
	} else {<br />
		if (!*line)<br />
			printf(&raquo;         :\n&raquo;);<br />
		else<br />
			printf(&raquo;         :	%s\n&raquo;, line);<br />
	}</p>
<p>	return 0;<br />
}</p>
<p>static struct rb_root root_sym_ext;</p>
<p>static void insert_source_line(struct sym_ext *sym_ext)<br />
{<br />
	struct sym_ext *iter;<br />
	struct rb_node **p = &#038;root_sym_ext.rb_node;<br />
	struct rb_node *parent = NULL;</p>
<p>	while (*p != NULL) {<br />
		parent = *p;<br />
		iter = rb_entry(parent, struct sym_ext, node);</p>
<p>		if (sym_ext->percent > iter->percent)<br />
			p = &#038;(*p)->rb_left;<br />
		else<br />
			p = &#038;(*p)->rb_right;<br />
	}</p>
<p>	rb_link_node(&#038;sym_ext->node, parent, p);<br />
	rb_insert_color(&#038;sym_ext->node, &#038;root_sym_ext);<br />
}</p>
<p>static void free_source_line(struct symbol *sym, int len)<br />
{<br />
	struct sym_ext *sym_ext = sym->priv;<br />
	int i;</p>
<p>	if (!sym_ext)<br />
		return;</p>
<p>	for (i = 0; i < len; i++)<br />
		free(sym_ext[i].path);<br />
	free(sym_ext);</p>
<p>	sym->priv = NULL;<br />
	root_sym_ext = RB_ROOT;<br />
}</p>
<p>/* Get the filename:line for the colored entries */<br />
static void<br />
get_source_line(struct symbol *sym, u64 start, int len, const char *filename)<br />
{<br />
	int i;<br />
	char cmd[PATH_MAX * 2];<br />
	struct sym_ext *sym_ext;</p>
<p>	if (!sym->hist_sum)<br />
		return;</p>
<p>	sym->priv = calloc(len, sizeof(struct sym_ext));<br />
	if (!sym->priv)<br />
		return;</p>
<p>	sym_ext = sym->priv;</p>
<p>	for (i = 0; i < len; i++) {<br />
		char *path = NULL;<br />
		size_t line_len;<br />
		u64 offset;<br />
		FILE *fp;</p>
<p>		sym_ext[i].percent = 100.0 * sym->hist[i] / sym->hist_sum;<br />
		if (sym_ext[i].percent <= 0.5)<br />
			continue;</p>
<p>		offset = start + i;<br />
		sprintf(cmd, "addr2line -e %s %016llx", filename, offset);<br />
		fp = popen(cmd, "r");<br />
		if (!fp)<br />
			continue;</p>
<p>		if (getline(&#038;path, &#038;line_len, fp) < 0 || !line_len)<br />
			goto next;</p>
<p>		sym_ext[i].path = malloc(sizeof(char) * line_len + 1);<br />
		if (!sym_ext[i].path)<br />
			goto next;</p>
<p>		strcpy(sym_ext[i].path, path);<br />
		insert_source_line(&#038;sym_ext[i]);</p>
<p>	next:<br />
		pclose(fp);<br />
	}<br />
}</p>
<p>static void print_summary(const char *filename)<br />
{<br />
	struct sym_ext *sym_ext;<br />
	struct rb_node *node;</p>
<p>	printf("\nSorted summary for file %s\n", filename);<br />
	printf("----------------------------------------------\n\n");</p>
<p>	if (RB_EMPTY_ROOT(&#038;root_sym_ext)) {<br />
		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);<br />
		return;<br />
	}</p>
<p>	node = rb_first(&#038;root_sym_ext);<br />
	while (node) {<br />
		double percent;<br />
		const char *color;<br />
		char *path;</p>
<p>		sym_ext = rb_entry(node, struct sym_ext, node);<br />
		percent = sym_ext->percent;<br />
		color = get_percent_color(percent);<br />
		path = sym_ext->path;</p>
<p>		color_fprintf(stdout, color, &raquo; %7.2f %s&raquo;, percent, path);<br />
		node = rb_next(node);<br />
	}<br />
}</p>
<p>static void annotate_sym(struct dso *dso, struct symbol *sym)<br />
{<br />
	const char *filename = dso->name, *d_filename;<br />
	u64 start, end, len;<br />
	char command[PATH_MAX*2];<br />
	FILE *file;</p>
<p>	if (!filename)<br />
		return;<br />
	if (sym->module)<br />
		filename = sym->module->path;<br />
	else if (dso == kernel_dso)<br />
		filename = vmlinux_name;</p>
<p>	start = sym->obj_start;<br />
	if (!start)<br />
		start = sym->start;<br />
	if (full_paths)<br />
		d_filename = filename;<br />
	else<br />
		d_filename = basename(filename);</p>
<p>	end = start + sym->end &#8211; sym->start + 1;<br />
	len = sym->end &#8211; sym->start;</p>
<p>	if (print_line) {<br />
		get_source_line(sym, start, len, filename);<br />
		print_summary(filename);<br />
	}</p>
<p>	printf(&raquo;\n\n&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;\n&raquo;);<br />
	printf(&raquo; Percent |	Source code &#038; Disassembly of %s\n&raquo;, d_filename);<br />
	printf(&raquo;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;\n&raquo;);</p>
<p>	if (verbose >= 2)<br />
		printf(&raquo;annotating [%p] %30s : [%p] %30s\n&raquo;, dso, dso->name, sym, sym->name);</p>
<p>	sprintf(command, &laquo;objdump &#8211;start-address=0x%016Lx &#8211;stop-address=0x%016Lx -dS %s|grep -v %s&raquo;,<br />
			(u64)start, (u64)end, filename, filename);</p>
<p>	if (verbose >= 3)<br />
		printf(&raquo;doing: %s\n&raquo;, command);</p>
<p>	file = popen(command, &laquo;r&raquo;);<br />
	if (!file)<br />
		return;</p>
<p>	while (!feof(file)) {<br />
		if (parse_line(file, sym, start, len) < 0)<br />
			break;<br />
	}</p>
<p>	pclose(file);<br />
	if (print_line)<br />
		free_source_line(sym, len);<br />
}</p>
<p>static void find_annotations(void)<br />
{<br />
	struct rb_node *nd;<br />
	struct dso *dso;<br />
	int count = 0;</p>
<p>	list_for_each_entry(dso, &#038;dsos, node) {</p>
<p>		for (nd = rb_first(&#038;dso->syms); nd; nd = rb_next(nd)) {<br />
			struct symbol *sym = rb_entry(nd, struct symbol, rb_node);</p>
<p>			if (sym->hist) {<br />
				annotate_sym(dso, sym);<br />
				count++;<br />
			}<br />
		}<br />
	}</p>
<p>	if (!count)<br />
		printf(&raquo; Error: symbol &#8216;%s&#8217; not present amongst the samples.\n&raquo;, sym_hist_filter);<br />
}</p>
<p>static int __cmd_annotate(void)<br />
{<br />
	int ret, rc = EXIT_FAILURE;<br />
	unsigned long offset = 0;<br />
	unsigned long head = 0;<br />
	struct stat input_stat;<br />
	event_t *event;<br />
	uint32_t size;<br />
	char *buf;</p>
<p>	register_idle_thread(&#038;threads, &#038;last_match);</p>
<p>	input = open(input_name, O_RDONLY);<br />
	if (input < 0) {<br />
		perror("failed to open file");<br />
		exit(-1);<br />
	}</p>
<p>	ret = fstat(input, &#038;input_stat);<br />
	if (ret < 0) {<br />
		perror("failed to stat file");<br />
		exit(-1);<br />
	}</p>
<p>	if (!force &#038;&#038; input_stat.st_uid &#038;&#038; (input_stat.st_uid != geteuid())) {<br />
		fprintf(stderr, "file: %s not owned by current user or root\n", input_name);<br />
		exit(-1);<br />
	}</p>
<p>	if (!input_stat.st_size) {<br />
		fprintf(stderr, "zero-sized file, nothing to do!\n");<br />
		exit(0);<br />
	}</p>
<p>	if (load_kernel() < 0) {<br />
		perror("failed to load kernel symbols");<br />
		return EXIT_FAILURE;<br />
	}</p>
<p>remap:<br />
	buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,<br />
			   MAP_SHARED, input, offset);<br />
	if (buf == MAP_FAILED) {<br />
		perror("failed to mmap file");<br />
		exit(-1);<br />
	}</p>
<p>more:<br />
	event = (event_t *)(buf + head);</p>
<p>	size = event->header.size;<br />
	if (!size)<br />
		size = 8;</p>
<p>	if (head + event->header.size >= page_size * mmap_window) {<br />
		unsigned long shift = page_size * (head / page_size);<br />
		int munmap_ret;</p>
<p>		munmap_ret = munmap(buf, page_size * mmap_window);<br />
		assert(munmap_ret == 0);</p>
<p>		offset += shift;<br />
		head -= shift;<br />
		goto remap;<br />
	}</p>
<p>	size = event->header.size;</p>
<p>	dump_printf(&raquo;%p [%p]: event: %d\n&raquo;,<br />
			(void *)(offset + head),<br />
			(void *)(long)event->header.size,<br />
			event->header.type);</p>
<p>	if (!size || process_event(event, offset, head) < 0) {</p>
<p>		dump_printf("%p [%p]: skipping unknown header type: %d\n",<br />
			(void *)(offset + head),<br />
			(void *)(long)(event->header.size),<br />
			event->header.type);</p>
<p>		total_unknown++;</p>
<p>		/*<br />
		 * assume we lost track of the stream, check alignment, and<br />
		 * increment a single u64 in the hope to catch on again &#8217;soon&#8217;.<br />
		 */</p>
<p>		if (unlikely(head &#038; 7))<br />
			head &#038;= ~7ULL;</p>
<p>		size = 8;<br />
	}</p>
<p>	head += size;</p>
<p>	if (offset + head < (unsigned long)input_stat.st_size)<br />
		goto more;</p>
<p>	rc = EXIT_SUCCESS;<br />
	close(input);</p>
<p>	dump_printf("      IP events: %10ld\n", total);<br />
	dump_printf("    mmap events: %10ld\n", total_mmap);<br />
	dump_printf("    comm events: %10ld\n", total_comm);<br />
	dump_printf("    fork events: %10ld\n", total_fork);<br />
	dump_printf(" unknown events: %10ld\n", total_unknown);</p>
<p>	if (dump_trace)<br />
		return 0;</p>
<p>	if (verbose >= 3)<br />
		threads__fprintf(stdout, &#038;threads);</p>
<p>	if (verbose >= 2)<br />
		dsos__fprintf(stdout);</p>
<p>	collapse__resort();<br />
	output__resort();</p>
<p>	find_annotations();</p>
<p>	return rc;<br />
}</p>
<p>static const char * const annotate_usage[] = {<br />
	&laquo;perf annotate [<options>] <command>&laquo;,<br />
	NULL<br />
};</p>
<p>static const struct option options[] = {<br />
	OPT_STRING(&#8217;i', &laquo;input&raquo;, &#038;input_name, &laquo;file&raquo;,<br />
		    &laquo;input file name&raquo;),<br />
	OPT_STRING(&#8217;s&#8217;, &laquo;symbol&raquo;, &#038;sym_hist_filter, &laquo;symbol&raquo;,<br />
		    &laquo;symbol to annotate&raquo;),<br />
	OPT_BOOLEAN(&#8217;f', &laquo;force&raquo;, &#038;force, &laquo;don&#8217;t complain, do it&raquo;),<br />
	OPT_BOOLEAN(&#8217;v', &laquo;verbose&raquo;, &#038;verbose,<br />
		    &laquo;be more verbose (show symbol address, etc)&raquo;),<br />
	OPT_BOOLEAN(&#8217;D', &laquo;dump-raw-trace&raquo;, &#038;dump_trace,<br />
		    &laquo;dump raw trace in ASCII&raquo;),<br />
	OPT_STRING(&#8217;k', &laquo;vmlinux&raquo;, &#038;vmlinux_name, &laquo;file&raquo;, &laquo;vmlinux pathname&raquo;),<br />
	OPT_BOOLEAN(&#8217;m', &laquo;modules&raquo;, &#038;modules,<br />
		    &laquo;load module symbols &#8211; WARNING: use only with -k and LIVE kernel&raquo;),<br />
	OPT_BOOLEAN(&#8217;l', &laquo;print-line&raquo;, &#038;print_line,<br />
		    &laquo;print matching source lines (may be slow)&raquo;),<br />
	OPT_BOOLEAN(&#8217;P', &laquo;full-paths&raquo;, &#038;full_paths,<br />
		    &laquo;Don&#8217;t shorten the displayed pathnames&raquo;),<br />
	OPT_END()<br />
};</p>
<p>static void setup_sorting(void)<br />
{<br />
	char *tmp, *tok, *str = strdup(sort_order);</p>
<p>	for (tok = strtok_r(str, &laquo;, &laquo;, &#038;tmp);<br />
			tok; tok = strtok_r(NULL, &laquo;, &laquo;, &#038;tmp)) {<br />
		if (sort_dimension__add(tok) < 0) {<br />
			error("Unknown --sort key: `%s'", tok);<br />
			usage_with_options(annotate_usage, options);<br />
		}<br />
	}</p>
<p>	free(str);<br />
}</p>
<p>int cmd_annotate(int argc, const char **argv, const char *prefix __used)<br />
{<br />
	symbol__init();</p>
<p>	page_size = getpagesize();</p>
<p>	argc = parse_options(argc, argv, options, annotate_usage, 0);</p>
<p>	setup_sorting();</p>
<p>	if (argc) {<br />
		/*<br />
		 * Special case: if there's an argument left then assume tha<br />
		 * it's a symbol filter:<br />
		 */<br />
		if (argc > 1)<br />
			usage_with_options(annotate_usage, options);</p>
<p>		sym_hist_filter = argv[0];<br />
	}</p>
<p>	if (!sym_hist_filter)<br />
		usage_with_options(annotate_usage, options);</p>
<p>	setup_pager();</p>
<p>	return __cmd_annotate();<br />
}</p>
]]></content:encoded>
			<wfw:commentRss>http://lynyrd.ru/builtin-annotate-c/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
